xhj il y a 6 mois
Parent
commit
efb2b77310
40 fichiers modifiés avec 15683 ajouts et 28 suppressions
  1. 229 0
      library/include/libiec61850/goose_publisher.h
  2. 200 0
      library/include/libiec61850/goose_receiver.h
  3. 318 0
      library/include/libiec61850/goose_subscriber.h
  4. 51 0
      library/include/libiec61850/hal_base.h
  5. 190 0
      library/include/libiec61850/hal_ethernet.h
  6. 166 0
      library/include/libiec61850/hal_filesystem.h
  7. 393 0
      library/include/libiec61850/hal_socket.h
  8. 105 0
      library/include/libiec61850/hal_thread.h
  9. 80 0
      library/include/libiec61850/hal_time.h
  10. 660 0
      library/include/libiec61850/iec61850_cdc.h
  11. 3158 0
      library/include/libiec61850/iec61850_client.h
  12. 553 0
      library/include/libiec61850/iec61850_common.h
  13. 66 0
      library/include/libiec61850/iec61850_config_file_parser.h
  14. 560 0
      library/include/libiec61850/iec61850_dynamic_model.h
  15. 632 0
      library/include/libiec61850/iec61850_model.h
  16. 2089 0
      library/include/libiec61850/iec61850_server.h
  17. 304 0
      library/include/libiec61850/iso_connection_parameters.h
  18. 49 0
      library/include/libiec61850/libiec61850_common_api.h
  19. 193 0
      library/include/libiec61850/linked_list.h
  20. 213 0
      library/include/libiec61850/logging_api.h
  21. 1333 0
      library/include/libiec61850/mms_client_connection.h
  22. 181 0
      library/include/libiec61850/mms_common.h
  23. 422 0
      library/include/libiec61850/mms_server.h
  24. 164 0
      library/include/libiec61850/mms_type_spec.h
  25. 81 0
      library/include/libiec61850/mms_types.h
  26. 1075 0
      library/include/libiec61850/mms_value.h
  27. 244 0
      library/include/libiec61850/r_session.h
  28. 73 0
      library/include/libiec61850/sntp_client.h
  29. 510 0
      library/include/libiec61850/sv_publisher.h
  30. 569 0
      library/include/libiec61850/sv_subscriber.h
  31. 262 0
      library/include/libiec61850/tls_ciphers.h
  32. 341 0
      library/include/libiec61850/tls_config.h
  33. BIN
      library/lib/libiec61850.so
  34. BIN
      library/lib/libiec61850.so.1.6.0
  35. 1 0
      modules/gateway-scheduler/CMakeLists.txt
  36. 30 0
      modules/gateway-scheduler/Makefile
  37. 18 3
      modules/gateway-scheduler/src/main.cpp
  38. 103 0
      modules/gateway-scheduler/src/report/MyIec61850Server.cpp
  39. 42 0
      modules/gateway-scheduler/src/report/MyIec61850Server.h
  40. 25 25
      modules/gateway-scheduler/src/service/QueueService.cpp

+ 229 - 0
library/include/libiec61850/goose_publisher.h

@@ -0,0 +1,229 @@
+/*
+ *  goose_publisher.h
+ *
+ *  Copyright 2013-2022 Michael Zillgith
+ *
+ *  This file is part of libIEC61850.
+ *
+ *  libIEC61850 is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  libIEC61850 is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with libIEC61850.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef GOOSE_PUBLISHER_H_
+#define GOOSE_PUBLISHER_H_
+
+#include "iec61850_common.h"
+#include "r_session.h"
+#include "linked_list.h"
+#include "mms_value.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef GOOSE_SV_COMM_PARAMETERS
+#define GOOSE_SV_COMM_PARAMETERS
+
+typedef struct sCommParameters {
+    uint8_t vlanPriority;
+    uint16_t vlanId;
+    uint16_t appId;
+    uint8_t dstAddress[6];
+} CommParameters;
+
+#endif
+
+typedef struct sGoosePublisher* GoosePublisher;
+
+/**
+ * \brief Create a new GoosePublisher instance
+ *
+ * NOTE: The created GoosePublisher instance uses VLAN tags
+ *
+ * \param parameters GOOSE communication parameters
+ * \param interfaceId name of the Ethernet interface to use (e.g. "eth0")
+ * 
+ * \return the new GoosePublisher instance
+ */
+LIB61850_API GoosePublisher
+GoosePublisher_create(CommParameters* parameters, const char* interfaceID);
+
+/**
+ * \brief Create a new GoosePublisher instance for Ethernet GOOSE
+ *
+ * \param parameters GOOSE communication parameters
+ * \param interfaceId name of the Ethernet interface to use (e.g. "eth0")
+ * \param useVlanTag enable or disable the usage of VLAN tags in GOOSE messages
+ * 
+ * \return the new GoosePublisher instance
+ */
+LIB61850_API GoosePublisher
+GoosePublisher_createEx(CommParameters* parameters, const char* interfaceID, bool useVlanTag);
+
+/**
+ * \brief Create a new GoosePublisher instance for R-GOOSE
+ *
+ * \param session R-session protocol instance to use
+ * \param appId the appID value to use
+ * 
+ * \return the new GoosePublisher instance
+ */
+LIB61850_API GoosePublisher
+GoosePublisher_createRemote(RSession session, uint16_t appId);
+
+/**
+ * \brief Release all resources of the GoosePublisher instance
+ *
+ * \param self GoosePublisher instance
+ */
+LIB61850_API void
+GoosePublisher_destroy(GoosePublisher self);
+
+/**
+ * \brief Publish a GOOSE message
+ *
+ * NOTE: This function also increased the sequence number of the GOOSE publisher
+ *
+ * \param self GoosePublisher instance
+ * \param dataSet the GOOSE data set to send
+ */
+LIB61850_API int
+GoosePublisher_publish(GoosePublisher self, LinkedList dataSet);
+
+/**
+ * \brief Publish a GOOSE message and store the sent message in the provided buffer
+ *
+ * \param self GoosePublisher instance
+ * \param dataSet the GOOSE data set to send
+ * \param msgBuf to store the sent message
+ * \param[out] msgLen the length of the sent message
+ * \param bufSize the size of the buffer to store the sent message
+ */
+LIB61850_API int
+GoosePublisher_publishAndDump(GoosePublisher self, LinkedList dataSet, char* msgBuf, int32_t* msgLen, int32_t bufSize);
+
+/**
+ * \brief Sets the GoID used by the GoosePublisher instance
+ *
+ * \param self GoosePublisher instance
+ * \param goID the GoId string
+ */
+LIB61850_API void
+GoosePublisher_setGoID(GoosePublisher self, char* goID);
+
+/**
+ * \brief Sets the GoCB reference used by the GoosePublisher instance
+ *
+ * \param self GoosePublisher instance
+ * \param goCbRef the GoCB reference string
+ */
+LIB61850_API void
+GoosePublisher_setGoCbRef(GoosePublisher self, char* goCbRef);
+
+/**
+ * \brief Sets the time allowed to live value of the GOOSE messages
+ *
+ * \param self GoosePublisher instance
+ * \param timeAllowedToLive the time allowed to live value in milliseconds
+ */
+LIB61850_API void
+GoosePublisher_setTimeAllowedToLive(GoosePublisher self, uint32_t timeAllowedToLive);
+
+/**
+ * \brief Sets the data set reference used by the GoosePublisher instance
+ *
+ * \param self GoosePublisher instance
+ * \param dataSetRef the data set reference string
+ */
+LIB61850_API void
+GoosePublisher_setDataSetRef(GoosePublisher self, char* dataSetRef);
+
+/**
+ * \brief Sets the configuration revision used by the GoosePublisher instance
+ *
+ * \param self GoosePublisher instance
+ * \param confRev the configuration revision value
+ */
+LIB61850_API void
+GoosePublisher_setConfRev(GoosePublisher self, uint32_t confRev);
+
+/**
+ * \brief Sets simulation flag in sent GOOSE messages
+ *
+ * \param self GoosePublisher instance
+ * \param simulation the value of the simulation flag
+ */
+LIB61850_API void
+GoosePublisher_setSimulation(GoosePublisher self, bool simulation);
+
+/**
+ * \brief Manually sets the state number (stNum) of the GoosePublisher instance
+ *
+ * NOTE: Only for testing! Use \ref GoosePublisher_increaseStNum instead whenever
+ * a data set member changes.
+ *
+ * \param self GoosePublisher instance
+ * \param stNum the state number of the next GOOSE message to send
+ */
+LIB61850_API void
+GoosePublisher_setStNum(GoosePublisher self, uint32_t stNum);
+
+/**
+ * \brief Manually sets the sequence number (sqNum) of the GoosePublisher instance
+ *
+ * NOTE: Only for testing! The sequence number is increase manually whenever \ref GoosePublisher_publish is called.
+ *
+ * \param self GoosePublisher instance
+ * \param sqNum the sequence number of the next GOOSE message to send
+ */
+LIB61850_API void
+GoosePublisher_setSqNum(GoosePublisher self, uint32_t sqNum);
+
+/**
+ * \brief Sets the needs commission flag in sent GOOSE messages
+ *
+ * \param self GoosePublisher instance
+ * \param ndsCom the value of the needs commission flag
+ */
+LIB61850_API void
+GoosePublisher_setNeedsCommission(GoosePublisher self, bool ndsCom);
+
+/**
+ * \brief Increase the state number (stNum) of the GoosePublisher instance
+ *
+ * The state number should be increased whenever a member of the GOOSE data set changed
+ *
+ * NOTE: This function also resets the sequence number (sqNum)
+ *
+ * \param self GoosePublisher instance
+ */
+LIB61850_API uint64_t
+GoosePublisher_increaseStNum(GoosePublisher self);
+
+/**
+ * \brief Reset state and sequence number of the GoosePublisher instance
+ *
+ * This function will set the state number (stNum) to 1 and the sequence number (sqNum) to 0.
+ *
+ * \param self GoosePublisher instance
+ */
+LIB61850_API void
+GoosePublisher_reset(GoosePublisher self);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GOOSE_PUBLISHER_H_ */

+ 200 - 0
library/include/libiec61850/goose_receiver.h

@@ -0,0 +1,200 @@
+/*
+ *  goose_receiver.h
+ *
+ *  Copyright 2014-2022 Michael Zillgith
+ *
+ *  This file is part of libIEC61850.
+ *
+ *  libIEC61850 is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  libIEC61850 is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with libIEC61850.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef GOOSE_RECEIVER_H_
+#define GOOSE_RECEIVER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h>
+
+#include "hal_ethernet.h"
+#include "goose_subscriber.h"
+#include "r_session.h"
+
+/**
+ * \addtogroup goose_api_group
+ */
+/**@{*/
+
+typedef struct sGooseReceiver* GooseReceiver;
+
+/**
+ * \brief Create a new receiver instance
+ *
+ * A GooseReceiver instance is used to handle all GOOSE messages received on a specific
+ * network interface.
+ *
+ * \return the new GooseReceiver instance
+ */
+LIB61850_API GooseReceiver
+GooseReceiver_create(void);
+
+/**
+ * \brief Create a new receiver instance using the provided buffer instead of allocating an own buffer
+ *
+ * A GooseReceiver instance is used to handle all GOOSE messages received on a specific
+ * network interface.
+ *
+ * \param buffer buffer to store Ethernet messages or NULL when using \ref GooseReceiver_handleMessage
+ *
+ * \return the new GooseReceiver instance
+ */
+LIB61850_API GooseReceiver
+GooseReceiver_createEx(uint8_t* buffer);
+
+/**
+ * \brief Create a new R-GOOSE receiver instance.
+ *
+ * \param session the remote session protocol instance
+ *
+ * \return the newly created receiver instance
+ */
+LIB61850_API GooseReceiver
+GooseReceiver_createRemote(RSession session);
+
+/**
+ * \brief sets the interface for the GOOSE receiver
+ *
+ * \param self the GooseReceiver instance
+ * \param interfaceId
+ */
+LIB61850_API void
+GooseReceiver_setInterfaceId(GooseReceiver self, const char* interfaceId);
+
+/**
+ * \brief return the interface ID used by the GOOSE receiver
+ *
+ * \param self the GosseReceiver instance
+ *
+ * \return the Ethernet interface ID string
+ */
+LIB61850_API const char*
+GooseReceiver_getInterfaceId(GooseReceiver self);
+
+/**
+ * \brief Add a subscriber to this receiver instance
+ *
+ * NOTE: Do not call this function while the receiver is running (after GooseReceiver_start
+ * has been called)!
+ *
+ * \param self the GooseReceiver instance
+ * \param subscriber the GooseSubscriber instance to add
+ */
+LIB61850_API void
+GooseReceiver_addSubscriber(GooseReceiver self, GooseSubscriber subscriber);
+
+/**
+ * \brief Remove a subscriber from this receiver instance
+ *
+ * NOTE: Do not call this function while the receiver is running (after GooseReceiver_start
+ * has been called)!
+ *
+ * \param self the GooseReceiver instance
+ * \param subscriber the GooseSubscriber instance to remove
+ */
+LIB61850_API void
+GooseReceiver_removeSubscriber(GooseReceiver self, GooseSubscriber subscriber);
+
+/**
+ * \brief start the GOOSE receiver in a separate thread
+ *
+ * \param self the GooseReceiver instance
+ */
+LIB61850_API void
+GooseReceiver_start(GooseReceiver self);
+
+/**
+ * \brief stop the GOOSE receiver running in a separate thread
+ *
+ * This function is used to stop the receiver thread started with GooseReceiver_start
+ *
+ * \param self the GooseReceiver instance
+ */
+LIB61850_API void
+GooseReceiver_stop(GooseReceiver self);
+
+/**
+ * \brief Check if GOOSE receiver is running
+ *
+ * Can be used to check if \ref GooseReceiver_start has been successful.
+ *
+ * \param self the GooseReceiver instance
+ *
+ * \return true if GOOSE receiver is running, false otherwise
+ */
+LIB61850_API bool
+GooseReceiver_isRunning(GooseReceiver self);
+
+/**
+ * \brief Free all resource of the GooseReceiver and all installed GooseSubscribers
+ *
+ * \param self the GooseReceiver instance
+ */
+LIB61850_API void
+GooseReceiver_destroy(GooseReceiver self);
+
+/***************************************
+ * Functions for non-threaded operation
+ ***************************************/
+LIB61850_API EthernetSocket
+GooseReceiver_startThreadless(GooseReceiver self);
+
+LIB61850_API void
+GooseReceiver_stopThreadless(GooseReceiver self);
+
+/**
+ * \brief Parse GOOSE messages if they are available
+ *
+ * Call after reception of an Ethernet frame or periodically
+ *
+ * \param self the receiver object
+ *
+ * \return true if a message was available and has been parsed, false otherwise
+ */
+LIB61850_API bool
+GooseReceiver_tick(GooseReceiver self);
+
+/**
+ * \brief Parse a GOOSE message
+ *
+ * Call after reception of an Ethernet frame (can be used as an alternative to \ref GooseReceiver_tick
+ * to avoid implementing the Ethernet HAL)
+ *
+ * \param self the receiver object
+ * \param buffer a buffer containing the complete Ethernet message
+ * \param size size of the Ethernet message
+ */
+LIB61850_API void
+GooseReceiver_handleMessage(GooseReceiver self, uint8_t* buffer, int size);
+
+/**@}*/
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* GOOSE_RECEIVER_H_ */

+ 318 - 0
library/include/libiec61850/goose_subscriber.h

@@ -0,0 +1,318 @@
+/*
+ *  goose_subscriber.h
+ *
+ *  Copyright 2013-2022 Michael Zillgith
+ *
+ *  This file is part of libIEC61850.
+ *
+ *  libIEC61850 is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  libIEC61850 is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with libIEC61850.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef GOOSE_SUBSCRIBER_H_
+#define GOOSE_SUBSCRIBER_H_
+
+#include "libiec61850_common_api.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \defgroup goose_api_group IEC 61850 GOOSE subscriber API
+ */
+/**@{*/
+
+#include "mms_value.h"
+
+typedef enum
+{
+    GOOSE_PARSE_ERROR_NO_ERROR = 0,
+    GOOSE_PARSE_ERROR_UNKNOWN_TAG,
+    GOOSE_PARSE_ERROR_TAGDECODE,
+    GOOSE_PARSE_ERROR_SUBLEVEL,
+    GOOSE_PARSE_ERROR_OVERFLOW,
+    GOOSE_PARSE_ERROR_UNDERFLOW,
+    GOOSE_PARSE_ERROR_TYPE_MISMATCH,
+    GOOSE_PARSE_ERROR_LENGTH_MISMATCH,
+    GOOSE_PARSE_ERROR_INVALID_PADDING
+} GooseParseError;
+
+typedef struct sGooseSubscriber* GooseSubscriber;
+
+/**
+ * \brief user provided callback function that will be invoked when a GOOSE message is received.
+ *
+ * \param subscriber the subscriber object that invoked the callback function,
+ * \param parameter a user provided parameter that will be passed to the callback function
+ */
+typedef void (*GooseListener)(GooseSubscriber subscriber, void* parameter);
+
+/**
+ * \brief create a new GOOSE subscriber instance.
+ *
+ * A new GOOSE subscriber will be created and connected to a specific GOOSE control block reference.
+ *
+ * The parameter goCbRef has to be given in MMS like notation (as it also will appear in the GOOSE message
+ * sent by the publisher). An example could be "simpleIOGenericIO/LLN0$GO$gcbEvents".
+ *
+ * The data set values contained in a GOOSE message will be written to the optionally provided MmsValue instance.
+ * The MmsValue object has to be of type MMS_ARRAY. The array elements need to be of the same type as
+ * the data set elements. It is intended that the provided MmsValue instance has been created by the
+ * IedConnection_getDataSet() method before.
+ *
+ * If NULL is given as dataSetValues it will be created the first time when a appropriate GOOSE message
+ * is received.
+ *
+ * \param goCbRef a string containing the object reference of the GOOSE Control Block (GoCB) in MMS notation the
+ *        GOOSE publisher uses.
+ * \param dataSetValues the MmsValue object where the data set values will be written or NULL.
+ */
+LIB61850_API GooseSubscriber
+GooseSubscriber_create(char* goCbRef, MmsValue* dataSetValues);
+
+/**
+ * \brief Get the GoId value of the received GOOSE message
+ *
+ * \param self GooseSubscriber instance to operate on.
+ */
+LIB61850_API char*
+GooseSubscriber_getGoId(GooseSubscriber self);
+
+/**
+ * \brief Get the GOOSE Control Block reference value of the received GOOSE message
+ *
+ * \param self GooseSubscriber instance to operate on.
+ */
+LIB61850_API char*
+GooseSubscriber_getGoCbRef(GooseSubscriber self);
+
+/**
+ * \brief Get the DatSet value of the received GOOSE message
+ *
+ * \param self GooseSubscriber instance to operate on.
+ */
+LIB61850_API char*
+GooseSubscriber_getDataSet(GooseSubscriber self);
+
+/**
+ * \brief set the destination mac address used by the subscriber to filter relevant messages.
+ *
+ * If dstMac is set the subscriber will ignore all messages with other dstMac values.
+ *
+ * \param self GooseSubscriber instance to operate on.
+ * \param dstMac the destination mac address
+ */
+LIB61850_API void
+GooseSubscriber_setDstMac(GooseSubscriber self, uint8_t dstMac[6]);
+
+/**
+ * \brief set the APPID used by the subscriber to filter relevant messages.
+ *
+ * If APPID is set the subscriber will ignore all messages with other APPID values.
+ *
+ * \param self GooseSubscriber instance to operate on.
+ * \param appId the APPID value the subscriber should use to filter messages
+ */
+LIB61850_API void
+GooseSubscriber_setAppId(GooseSubscriber self, uint16_t appId);
+
+/**
+ * \brief Check if subscriber state is valid
+ *
+ * A GOOSE subscriber is valid if TimeAllowedToLive timeout is not elapsed and GOOSE
+ * message were received with correct state and sequence ID.
+ *
+ */
+LIB61850_API bool
+GooseSubscriber_isValid(GooseSubscriber self);
+
+/**
+ * \brief Get parse error in case of invalid subscriber state
+ *
+ * \param self GooseSubscriber instance to operate on.
+ *
+ * \return the error code representing a message parse problem of the last received message
+ */
+LIB61850_API GooseParseError
+GooseSubscriber_getParseError(GooseSubscriber self);
+
+/**
+ * \brief Destroy the GooseSubscriber instance
+ *
+ * Do not call this function when the GooseSubscriber instance was added to a GooseReceiver.
+ * The GooseReceiver will call the destructor when \ref GooseReceiver_destroy is called!
+ *
+ * \param self GooseSubscriber instance to operate on.
+ */
+LIB61850_API void
+GooseSubscriber_destroy(GooseSubscriber self);
+
+/**
+ * \brief set a callback function that will be invoked when a GOOSE message has been received.
+ *
+ * \param self GooseSubscriber instance to operate on.
+ * \param listener user provided callback function
+ * \param parameter a user provided parameter that will be passed to the callback function
+ */
+LIB61850_API void
+GooseSubscriber_setListener(GooseSubscriber self, GooseListener listener, void* parameter);
+
+/**
+ * \brief Get the APPID value of the received GOOSE message
+ *
+ * \param self GooseSubscriber instance to operate on.
+ */
+LIB61850_API int32_t
+GooseSubscriber_getAppId(GooseSubscriber self);
+
+/**
+ * \brief Get the source MAC address of the received GOOSE message
+ *
+ * \param self GooseSubscriber instance to operate on.
+ * \param buffer buffer to store the MAC address (at least 6 byte)
+ */
+LIB61850_API void
+GooseSubscriber_getSrcMac(GooseSubscriber self, uint8_t* buffer);
+
+/**
+ * \brief Get the destination MAC address of the received GOOSE message
+ *
+ * \param self GooseSubscriber instance to operate on.
+ * \param buffer buffer to store the MAC address (at least 6 byte)
+ */
+LIB61850_API void
+GooseSubscriber_getDstMac(GooseSubscriber self, uint8_t* buffer);
+
+/**
+ * \brief return the state number (stNum) of the last received GOOSE message.
+ *
+ * The state number is increased if any of the values in the GOOSE data set changed due to a valid trigger condition
+ *
+ * \param self GooseSubscriber instance to operate on.
+ *
+ * \return the state number of the last received GOOSE message
+ */
+LIB61850_API uint32_t
+GooseSubscriber_getStNum(GooseSubscriber self);
+
+/**
+ * \brief return the sequence number (sqNum) of the last received GOOSE message.
+ *
+ * The sequence number is increased for every consecutive GOOSE message without state change. When a state change occurs (stNum is increased)
+ * then the sequence number (sqNum) will be reset.
+ *
+ * \param self GooseSubscriber instance to operate on.
+ *
+ * \return the sequence number of the last received GOOSE message
+ */
+LIB61850_API uint32_t
+GooseSubscriber_getSqNum(GooseSubscriber self);
+
+/**
+ * \brief returns the test flag of the last received GOOSE message
+ *
+ * IMPORTANT: Goose messages with test=TRUE have to be ignored to be standard compliant!
+ *
+ * \param self GooseSubscriber instance to operate on.
+ *
+ * \return the state of the test flag of the last received GOOSE message.
+ */
+LIB61850_API bool
+GooseSubscriber_isTest(GooseSubscriber self);
+
+/**
+ * \brief returns the confRev value of the last received GOOSE message
+ *
+ * \param self GooseSubscriber instance to operate on.
+ *
+ * \return the confRev value of the last received GOOSE message. If the message does not contain such
+ *         a value the result is always 0
+ */
+LIB61850_API uint32_t
+GooseSubscriber_getConfRev(GooseSubscriber self);
+
+/**
+ * \brief returns the value of the ndsCom (needs commission) flag of the last received GOOSE message.
+ *
+ * IMPORTANT: Goose messages with ndsCom=TRUE have to be ignored to be standard compliant!
+ *
+ * \param self GooseSubscriber instance to operate on.
+ *
+ * \return the state of the ndsCom flag of the last received GOOSE message.
+ *
+ */
+LIB61850_API bool
+GooseSubscriber_needsCommission(GooseSubscriber self);
+
+/**
+ * \brief Get the TimeAllowedToLive value of the last received message.
+ *
+ * \param self GooseSubscriber instance to operate on.
+ *
+ * \return the TimeAllowedToLive value of the last received GOOSE message in milliseconds.
+ */
+LIB61850_API uint32_t
+GooseSubscriber_getTimeAllowedToLive(GooseSubscriber self);
+
+/**
+ * \brief Get the timestamp of the last received message.
+ *
+ * \param self GooseSubscriber instance to operate on.
+ *
+ * \return the timestamp value of the last received GOOSE message in milliseconds since epoch (1.1.1970 UTC).
+ */
+LIB61850_API uint64_t
+GooseSubscriber_getTimestamp(GooseSubscriber self);
+
+/**
+ * \brief get the data set values received with the last report
+ *
+ * Note: To prevent data corruption. The MmsValue instance received should
+ * only be used inside of the callback function, when the GOOSE receiver is
+ * running in a separate thread.
+ *
+ * \param self GooseSubscriber instance to operate on.
+ *
+ * \return MmsValue instance of the report data set
+ */
+LIB61850_API MmsValue*
+GooseSubscriber_getDataSetValues(GooseSubscriber self);
+
+LIB61850_API bool
+GooseSubscriber_isVlanSet(GooseSubscriber self);
+
+LIB61850_API uint16_t
+GooseSubscriber_getVlanId(GooseSubscriber self);
+
+LIB61850_API uint8_t
+GooseSubscriber_getVlanPrio(GooseSubscriber self);
+
+/**
+ * \brief Configure the Subscriber to listen to any received GOOSE message
+ *
+ * NOTE: When the observer flag is set the subscriber also has access to the
+ * goCbRef, goId, and datSet values of the received GOOSE message
+ */
+LIB61850_API void
+GooseSubscriber_setObserver(GooseSubscriber self);
+#ifdef __cplusplus
+}
+#endif
+
+
+/**@}*/
+
+#endif /* GOOSE_SUBSCRIBER_H_ */

+ 51 - 0
library/include/libiec61850/hal_base.h

@@ -0,0 +1,51 @@
+/*
+ *  hal_base.h
+ *
+ *  Copyright 2013-2021 Michael Zillgith
+ *
+ *  This file is part of Platform Abstraction Layer (libpal)
+ *  for libiec61850, libmms, and lib60870.
+ */
+
+#ifndef HAL_INC_HAL_BASE_H_
+#define HAL_INC_HAL_BASE_H_
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+#ifdef __GNUC__
+#define ATTRIBUTE_PACKED __attribute__ ((__packed__))
+#else
+#define ATTRIBUTE_PACKED
+#endif
+
+#ifndef DEPRECATED
+#if defined(__GNUC__) || defined(__clang__)
+  #define DEPRECATED __attribute__((deprecated))
+#else
+  #define DEPRECATED
+#endif
+#endif
+
+#if defined _WIN32 || defined __CYGWIN__
+    #ifdef EXPORT_FUNCTIONS_FOR_DLL
+        #define PAL_API __declspec(dllexport)
+    #else
+        #define PAL_API
+    #endif
+
+    #define PAL_INTERNAL
+#else
+    #if __GNUC__ >= 4
+        #define PAL_API __attribute__ ((visibility ("default")))
+        #define PAL_INTERNAL  __attribute__ ((visibility ("hidden")))
+    #else
+        #define PAL_API
+        #define PAL_INTERNAL
+    #endif
+#endif
+
+#endif /* HAL_INC_HAL_BASE_H_ */

+ 190 - 0
library/include/libiec61850/hal_ethernet.h

@@ -0,0 +1,190 @@
+/*
+ *  ethernet_hal.h
+ *
+ *  Copyright 2013-2021 Michael Zillgith
+ *
+ *  This file is part of Platform Abstraction Layer (libpal)
+ *  for libiec61850, libmms, and lib60870.
+ */
+
+#ifndef ETHERNET_HAL_H_
+#define ETHERNET_HAL_H_
+
+#include "hal_base.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*! \addtogroup hal
+   *
+   *  @{
+   */
+
+/**
+ * @defgroup HAL_ETHERNET Direct access to the Ethernet layer (optional - required by GOOSE and Sampled Values)
+ *
+ * @{
+ */
+
+
+/**
+ * \brief Opaque handle that represents an Ethernet "socket".
+ */
+typedef struct sEthernetSocket* EthernetSocket;
+
+/** Opaque reference for a set of Ethernet socket handles */
+typedef struct sEthernetHandleSet* EthernetHandleSet;
+
+typedef enum {
+    ETHERNET_SOCKET_MODE_PROMISC, /**<< receive all Ethernet messages */
+    ETHERNET_SOCKET_MODE_ALL_MULTICAST, /**<< receive all multicast messages */
+    ETHERNET_SOCKET_MODE_MULTICAST, /**<< receive only specific multicast messages */
+    ETHERNET_SOCKET_MODE_HOST_ONLY /**<< receive only messages for the host */
+} EthernetSocketMode;
+
+/**
+ * \brief Create a new connection handle set (EthernetHandleSet)
+ *
+ * \return new EthernetHandleSet instance
+ */
+PAL_API EthernetHandleSet
+EthernetHandleSet_new(void);
+
+/**
+ * \brief add a socket to an existing handle set
+ *
+ * \param self the HandleSet instance
+ * \param sock the socket to add
+ */
+PAL_API void
+EthernetHandleSet_addSocket(EthernetHandleSet self, const EthernetSocket sock);
+
+/**
+ * \brief remove a socket from an existing handle set
+ *
+ * \param self the HandleSet instance
+ * \param sock the socket to add
+ */
+PAL_API void
+EthernetHandleSet_removeSocket(EthernetHandleSet self, const EthernetSocket sock);
+
+/**
+ * \brief wait for a socket to become ready
+ *
+ * This function is corresponding to the BSD socket select function.
+ * The function will return after \p timeoutMs ms if no data is pending.
+ *
+ * \param self the HandleSet instance
+ * \param timeoutMs in milliseconds (ms)
+ * \return It returns the number of sockets on which data is pending
+ *   or 0 if no data is pending on any of the monitored connections.
+ *   The function shall return -1 if a socket error occures.
+ */
+PAL_API int
+EthernetHandleSet_waitReady(EthernetHandleSet self, unsigned int timeoutMs);
+
+/**
+ * \brief destroy the EthernetHandleSet instance
+ *
+ * \param self the HandleSet instance to destroy
+ */
+PAL_API void
+EthernetHandleSet_destroy(EthernetHandleSet self);
+
+/**
+ * \brief Return the MAC address of an Ethernet interface.
+ *
+ * The result are the six bytes that make up the Ethernet MAC address.
+ *
+ * \param interfaceId the ID of the Ethernet interface
+ * \param addr pointer to a buffer to store the MAC address
+ */
+PAL_API void
+Ethernet_getInterfaceMACAddress(const char* interfaceId, uint8_t* addr);
+
+/**
+ * \brief Create an Ethernet socket using the specified interface and
+ * destination MAC address.
+ *
+ * \param interfaceId the ID of the Ethernet interface
+ * \param destAddress byte array that contains the Ethernet destination MAC address for sending
+ */
+PAL_API EthernetSocket
+Ethernet_createSocket(const char* interfaceId, uint8_t* destAddress);
+
+/**
+ * \brief destroy the ethernet socket
+ *
+ * \param ethSocket the ethernet socket handle
+ */
+PAL_API void
+Ethernet_destroySocket(EthernetSocket ethSocket);
+
+PAL_API void
+Ethernet_sendPacket(EthernetSocket ethSocket, uint8_t* buffer, int packetSize);
+
+/*
+ * \brief set the receive mode of the Ethernet socket
+ *
+ * NOTE: When not implemented the the implementation has to be able to receive
+ * all messages required by GOOSE and/or SV (usually multicast addresses).
+ *
+ * \param ethSocket the ethernet socket handle
+ * \param mode the mode of the socket
+ */
+PAL_API void
+Ethernet_setMode(EthernetSocket ethSocket, EthernetSocketMode mode);
+
+/**
+ * \brief Add a multicast address to be received by the Ethernet socket
+ *
+ * Used when mode is ETHERNET_SOCKET_MODE_MULTICAST
+ *
+ * \param ethSocket the ethernet socket handle
+ * \param multicastAddress the multicast Ethernet address (this has to be a byte buffer of at least 6 byte)
+ */
+PAL_API void
+Ethernet_addMulticastAddress(EthernetSocket ethSocket, uint8_t* multicastAddress);
+
+/*
+ * \brief set a protocol filter for the specified etherType
+ *
+ * NOTE: Implementation is not required but can improve the performance when the ethertype
+ * filtering can be done on OS/network stack layer.
+ *
+ * \param ethSocket the ethernet socket handle
+ * \param etherType the ether type of messages to accept
+ */
+PAL_API 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 bufferSize the maximum size of the buffer
+ *
+ * \return size of message received in bytes
+ */
+PAL_API int
+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
+ */
+PAL_API bool
+Ethernet_isSupported(void);
+
+/*! @} */
+
+/*! @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETHERNET_HAL_H_ */

+ 166 - 0
library/include/libiec61850/hal_filesystem.h

@@ -0,0 +1,166 @@
+/*
+ *  filesystem_hal.h
+ *
+ *  Copyright 2013-2021 Michael Zillgith
+ *
+ *  This file is part of Platform Abstraction Layer (libpal)
+ *  for libiec61850, libmms, and lib60870.
+ */
+
+#ifndef FILESYSTEM_HAL_H_
+#define FILESYSTEM_HAL_H_
+
+#include "hal_base.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*! \addtogroup hal
+   *
+   *  @{
+   */
+
+/**
+ * @defgroup HAL_FILESYSTEM Interface to the native file system (optional)
+ *
+ * @{
+ */
+
+typedef void* FileHandle;
+typedef struct sDirectoryHandle* DirectoryHandle;
+
+#ifndef CONFIG_SYSTEM_FILE_SEPARATOR
+#define CONFIG_SYSTEM_FILE_SEPARATOR '/'
+#endif
+
+/**
+ * \brief open a file
+ *
+ * \param pathName full name (path + filename) of the file
+ * \param readWrite true opens the file with read and write access - false opens for read access only
+ *
+ * \return a handle for the file. Has to be used by subsequent calls to file functions to identify the file or
+ *         NULL if opening fails
+ */
+PAL_API FileHandle
+FileSystem_openFile(char* pathName, bool readWrite);
+
+/**
+ * \brief read from an open file
+ *
+ * This function will read the next block of the file. The maximum number of bytes to read
+ * is given. A call to this function will move the file position by the number of bytes read.
+ * If the file position reaches the end of file then subsequent calls of this function shall
+ * return 0.
+ *
+ * \param handle the file handle to identify the file
+ * \param buffer the buffer to write the read data
+ * \param maxSize maximum number of bytes to read
+ *
+ * \return the number of bytes actually read
+ */
+PAL_API int
+FileSystem_readFile(FileHandle handle, uint8_t* buffer, int maxSize);
+
+/**
+ * \brief write to an open file
+ *
+ * \param handle the file handle to identify the file
+ * \param buffer the buffer with the data to write
+ * \param size the number of bytes to write
+ *
+ * \return the number of bytes actually written
+ */
+PAL_API int
+FileSystem_writeFile(FileHandle handle, uint8_t* buffer, int size);
+
+/**
+ * \brief close an open file
+ *
+ * \param handle the file handle to identify the file
+ */
+PAL_API void
+FileSystem_closeFile(FileHandle handle);
+
+/**
+ * \brief return attributes of the given file
+ *
+ * This function is used by the MMS layer to determine basic file attributes.
+ * The size of the file has to be returned in bytes. The timestamp of the last modification has
+ * to be returned as milliseconds since Unix epoch - or 0 if this function is not supported.
+ *
+ * \param pathName full name (path + filename) of the file
+ * \param fileSize a pointer where to store the file size
+ * \param lastModificationTimestamp is used to store the timestamp of last modification of the file
+ *
+ * \return true if file exists, false if not
+ */
+PAL_API bool
+FileSystem_getFileInfo(char* filename, uint32_t* fileSize, uint64_t* lastModificationTimestamp);
+
+/**
+ * \brief delete a file
+ *
+ * \param pathName full name (path + filename) of the file
+ *
+ * \return true on success, false on error
+ */
+PAL_API bool
+FileSystem_deleteFile(char* filename);
+
+/**
+ * \brief rename a file
+ *
+ * \param oldFileName current full name (path + filename) of the file
+ * \param newFileName new full name (path + filename) of the file
+ *
+ * \return true on success, false on error
+ */
+PAL_API bool
+FileSystem_renameFile(char* oldFilename, char* newFilename);
+
+/**
+ * \brief open the directoy with the specified name
+ *
+ * \param directoryName
+ *
+ * \return a handle for the opened directory to be used in subsequent calls to identify the directory
+ */
+PAL_API DirectoryHandle
+FileSystem_openDirectory(char* directoryName);
+
+/**
+ * \brief read the next directory entry
+ *
+ * This function returns the next directory entry. The entry is only a valid pointer as long as the
+ * FileSystem_closeDirectory or another FileSystem_readDirectory function is not called for the given
+ * DirectoryHandle.
+ *
+ * \param directory the handle to identify the directory
+ * \param isDirectory return value that indicates if the directory entry is itself a directory (true)
+ *
+ * \return the name of the directory entry
+ */
+PAL_API char*
+FileSystem_readDirectory(DirectoryHandle directory, bool* isDirectory);
+
+
+/**
+ * \brief close a directory
+ *
+ * \param directory the handle to identify the directory
+ */
+PAL_API void
+FileSystem_closeDirectory(DirectoryHandle directory);
+
+
+/*! @} */
+
+/*! @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FILESYSTEM_HAL_H_ */

+ 393 - 0
library/include/libiec61850/hal_socket.h

@@ -0,0 +1,393 @@
+/*
+ *  socket_hal.h
+ *
+ *  Copyright 2013-2022 Michael Zillgith
+ *
+ *  This file is part of Platform Abstraction Layer (libpal)
+ *  for libiec61850, libmms, and lib60870.
+ */
+
+#ifndef SOCKET_HAL_H_
+#define SOCKET_HAL_H_
+
+#include "hal_base.h"
+
+/**
+ * \file hal_socket.h
+ * \brief Abstraction layer TCP/IP sockets
+ * Has to be implemented for CS 104 TCP/IP.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*! \defgroup hal Platform (Hardware/OS) abstraction layer
+   *
+   *  Platform abstraction layer. These functions have to be implemented when the library is
+   *  to be ported to new platforms. It might not be required to implement all interfaces
+   *  depending on the required library features.
+   *
+   *  @{
+   */
+
+/**
+ * @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 lib60870 to a new hardware/OS platform when TCP/IP is required.
+ *
+ * @{
+ */
+
+/** Opaque reference for a server socket instance */
+typedef struct sServerSocket* ServerSocket;
+
+typedef struct sUdpSocket* UdpSocket;
+
+/** 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;
+
+/** State of an asynchronous connect */
+typedef enum
+{
+    SOCKET_STATE_CONNECTING = 0,
+    SOCKET_STATE_FAILED = 1,
+    SOCKET_STATE_CONNECTED = 2
+} SocketState;
+
+
+/**
+ * \brief Create a new connection handle set (HandleSet)
+ *
+ * \return new HandleSet instance
+ */
+PAL_API HandleSet
+Handleset_new(void);
+
+/**
+ * \brief Reset the handle set for reuse
+ */
+PAL_API void
+Handleset_reset(HandleSet self);
+
+/**
+ * \brief add a socket to an existing handle set
+ *
+ * \param self the HandleSet instance
+ * \param sock the socket to add
+ */
+PAL_API void
+Handleset_addSocket(HandleSet self, const Socket sock);
+
+/**
+ * \brief remove a socket from an existing handle set
+ */
+void
+Handleset_removeSocket(HandleSet self, const Socket sock);
+
+/**
+ * \brief wait for a socket to become ready
+ *
+ * This function is corresponding to the BSD socket select function.
+ * It returns the number of sockets on which data is pending or 0 if no data is pending
+ * on any of the monitored connections. The function will return after "timeout" ms if no
+ * data is pending.
+ * The function shall return -1 if a socket error occures.
+ *
+ * \param self the HandleSet instance
+ * \param timeout in milliseconds (ms)
+ * \return It returns the number of sockets on which data is pending
+ *   or 0 if no data is pending on any of the monitored connections.
+ *   The function shall return -1 if a socket error occures.
+ */
+PAL_API int
+Handleset_waitReady(HandleSet self, unsigned int timeoutMs);
+
+/**
+ * \brief destroy the HandleSet instance
+ *
+ * \param self the HandleSet instance to destroy
+ */
+PAL_API 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
+ */
+PAL_API ServerSocket
+TcpServerSocket_create(const char* address, int port);
+
+/**
+ * \brief Create an IPv4 UDP socket instance
+ *
+ * \return new UDP socket instance
+ */
+PAL_API UdpSocket
+UdpSocket_create(void);
+
+/**
+ * \brief Create an IPv6 UDP socket instance
+ *
+ * \return new UDP socket instance
+ */
+PAL_API UdpSocket
+UdpSocket_createIpV6(void);
+
+/**
+ * \brief Add the socket to an IPv4 or IPv6 multicast group
+ *
+ * \param self UDP socket instance
+ * \param multicastAddress IPv4 or IPv6 multicast address
+ *
+ * \return true on success, false otherwise
+ */
+PAL_API bool
+UdpSocket_addGroupMembership(UdpSocket self, const char* multicastAddress);
+
+/**
+ * \brief Sets the multicast TTL (number of hops) for this UDP socket
+ *
+ * \param self UDP socket instance
+ * \param ttl number of hops for multicast messages. Default is 1 (not routable!)
+ *
+ * \return true on success, false otherwise
+ */
+PAL_API bool
+UdpSocket_setMulticastTtl(UdpSocket self, int ttl);
+
+PAL_API bool
+UdpSocket_bind(UdpSocket self, const char* address, int port);
+
+PAL_API bool
+UdpSocket_sendTo(UdpSocket self, const char* address, int port, uint8_t* msg, int msgSize);
+
+/**
+ * \brief Receive data from UDP socket (store data and (optionally) the IP address of the sender
+ *
+ * \param self UDP socket instance
+ * \param address (optional) buffer to store the IP address as string
+ * \param maxAddrSize (optional) size of the provided buffer to store the IP address
+ * \param msg buffer to store the UDP message data
+ * \param msgSize the maximum size of the message to receive
+ *
+ * \return number of received bytes or -1 in case of an error
+ */
+PAL_API int
+UdpSocket_receiveFrom(UdpSocket self, char* address, int maxAddrSize, uint8_t* msg, int msgSize);
+
+
+PAL_API 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
+ */
+PAL_API Socket
+ServerSocket_accept(ServerSocket self);
+
+/**
+ * \brief active TCP keep alive for socket and set keep alive parameters
+ *
+ * NOTE: implementation is mandatory for IEC 61850 MMS
+ *
+ * \param self server socket instance
+ * \param idleTime time (in s) between last received message and first keep alive message
+ * \param interval time (in s) between subsequent keep alive messages if no ACK received
+ * \param count number of not missing keep alive ACKs until socket is considered dead
+ */
+PAL_API void
+Socket_activateTcpKeepAlive(Socket self, int idleTime, int interval, int count);
+
+/**
+ * \brief set the maximum number of pending connections 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
+ *
+ */
+PAL_API 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
+ */
+PAL_API 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.
+ */
+PAL_API 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
+ */
+PAL_API void
+Socket_setConnectTimeout(Socket self, uint32_t timeoutInMs);
+
+/**
+ * \brief bind a socket to a particular IP address and port (for TcpSocket)
+ * 
+ * NOTE: Don't use the socket when this functions returns false!
+ * 
+ * \param self the client socket instance
+ * \param srcAddress the local IP address or hostname as C string
+ * \param srcPort the local TCP port to use. When < 1 the OS will chose the TCP port to use.
+ * 
+ * \return true in case of success, false otherwise
+ */ 
+PAL_API bool
+Socket_bind(Socket self, const char* srcAddress, int srcPort);
+
+/**
+ * \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 true if the connection was established successfully, false otherwise
+ */
+PAL_API bool
+Socket_connect(Socket self, const char* address, int port);
+
+PAL_API bool
+Socket_connectAsync(Socket self, const char* address, int port);
+
+PAL_API SocketState
+Socket_checkAsyncConnectState(Socket self);
+
+/**
+ * \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
+ */
+PAL_API 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
+ */
+PAL_API int
+Socket_write(Socket self, uint8_t* buf, int size);
+
+PAL_API char*
+Socket_getLocalAddress(Socket self);
+
+/**
+ * \brief Get the address of the peer application (IP address and port number)
+ *
+ * The peer address has to be returned as null terminated string
+ *
+ * Implementation of this function is MANDATORY (libiec61850)
+ *
+ * \param self the client, connection or server socket instance
+ *
+ * \return the IP address and port number as strings separated by the ':' character.
+ */
+PAL_API char*
+Socket_getPeerAddress(Socket self);
+
+/**
+ * \brief Get the address of the peer application (IP address and port number)
+ *
+ * The peer address has to be returned as null terminated string
+ *
+ * Implementation of this function is MANDATORY (lib60870 and libiec61850)
+ *
+ * \param self the client, connection or server socket instance
+ * \param peerAddressString a string to store the peer address (the string should have space
+ *        for at least 60 characters)
+ *
+ * \return the IP address and port number as strings separated by the ':' character. If the
+ *         address is an IPv6 address the IP part is encapsulated in square brackets.
+ */
+PAL_API char*
+Socket_getPeerAddressStatic(Socket self, char* peerAddressString);
+
+/**
+ * \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
+ */
+PAL_API void
+Socket_destroy(Socket self);
+
+/*! @} */
+
+/*! @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SOCKET_HAL_H_ */

+ 105 - 0
library/include/libiec61850/hal_thread.h

@@ -0,0 +1,105 @@
+/*
+ *  thread_hal.h
+ *
+ *  Multi-threading abstraction layer
+ *
+ *  Copyright 2013-2021 Michael Zillgith
+ *
+ *  This file is part of Platform Abstraction Layer (libpal)
+ *  for libiec61850, libmms, and lib60870.
+ */
+
+#ifndef THREAD_HAL_H_
+#define THREAD_HAL_H_
+
+#include "hal_base.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file hal_thread.h
+ * \brief Abstraction layer for threading and synchronization
+ */
+
+/*! \addtogroup hal
+   *
+   *  @{
+   */
+
+/**
+ * @defgroup HAL_THREAD Threading and synchronization API
+ *
+ * @{
+ */
+
+/** Opaque reference of a Thread instance */
+typedef struct sThread* Thread;
+
+/** Qpaque reference of a Semaphore instance */
+typedef void* Semaphore;
+
+/** Reference to a function that is called when starting the thread */
+typedef void* (*ThreadExecutionFunction) (void*);
+
+/**
+ * \brief Create a new Thread instance
+ *
+ * \param function the entry point of the thread
+ * \param parameter a parameter that is passed to the threads start function
+ * \param autodestroy the thread is automatically destroyed if the ThreadExecutionFunction has finished.
+ *
+ * \return the newly created Thread instance
+ */
+PAL_API Thread
+Thread_create(ThreadExecutionFunction function, void* parameter, bool autodestroy);
+
+/**
+ * \brief Start a Thread.
+ *
+ * This function invokes the start function of the thread. The thread terminates when
+ * the start function returns.
+ *
+ * \param thread the Thread instance to start
+ */
+PAL_API void
+Thread_start(Thread thread);
+
+/**
+ * \brief Destroy a Thread and free all related resources.
+ *
+ * \param thread the Thread instance to destroy
+ */
+PAL_API void
+Thread_destroy(Thread thread);
+
+/**
+ * \brief Suspend execution of the Thread for the specified number of milliseconds
+ */
+PAL_API void
+Thread_sleep(int millies);
+
+PAL_API Semaphore
+Semaphore_create(int initialValue);
+
+/* Wait until semaphore value is greater than zero. Then decrease the semaphore value. */
+PAL_API void
+Semaphore_wait(Semaphore self);
+
+PAL_API void
+Semaphore_post(Semaphore self);
+
+PAL_API void
+Semaphore_destroy(Semaphore self);
+
+/*! @} */
+
+/*! @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* THREAD_HAL_H_ */

+ 80 - 0
library/include/libiec61850/hal_time.h

@@ -0,0 +1,80 @@
+/*
+ *  time.c
+ *
+ *  Copyright 2013-2022 Michael Zillgith
+ *
+ *  This file is part of Platform Abstraction Layer (libpal)
+ *  for libiec61850, libmms, and lib60870.
+ */
+
+#ifndef HAL_C_
+#define HAL_C_
+
+#include "hal_base.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file hal_time.h
+ * \brief Abstraction layer for system time access
+ */
+
+/*! \addtogroup hal
+   *
+   *  @{
+   */
+
+/**
+ * @defgroup HAL_TIME Time related functions
+ *
+ * @{
+ */
+
+typedef uint64_t nsSinceEpoch;
+typedef uint64_t msSinceEpoch;
+
+/**
+ * Get the system time in milliseconds.
+ *
+ * The time value returned as 64-bit unsigned integer should represent the milliseconds
+ * since the UNIX epoch (1970/01/01 00:00 UTC).
+ *
+ * \return the system time with millisecond resolution.
+ */
+PAL_API msSinceEpoch
+Hal_getTimeInMs(void);
+
+/**
+ * Get the system time in nanoseconds.
+ *
+ * The time value returned as 64-bit unsigned integer should represent the nanoseconds
+ * since the UNIX epoch (1970/01/01 00:00 UTC).
+ *
+ * \return the system time with nanosecond resolution.
+ */
+PAL_API nsSinceEpoch
+Hal_getTimeInNs(void);
+
+/**
+* Set the system time from ns time
+*
+* The time value returned as 64-bit unsigned integer should represent the nanoseconds
+* since the UNIX epoch (1970/01/01 00:00 UTC).
+*
+* \return true on success, otherwise false
+*/
+PAL_API bool
+Hal_setTimeInNs(nsSinceEpoch nsTime);
+
+/*! @} */
+
+/*! @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* HAL_C_ */

+ 660 - 0
library/include/libiec61850/iec61850_cdc.h

@@ -0,0 +1,660 @@
+/*
+ *  cdc.h
+ *
+ *  Copyright 2014 Michael Zillgith
+ *
+ *  This file is part of libIEC61850.
+ *
+ *  libIEC61850 is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  libIEC61850 is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with libIEC61850.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef CDC_H_
+#define CDC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/** \addtogroup server_api_group
+ *  @{
+ */
+
+/**
+ * @defgroup COMMON_DATA_CLASSES Helper functions to create common data classes (CDC) using the dynamic model API
+ *
+ * \brief Helper functions to create compliant common data classes (CDC) using the dynamic model API.
+ *
+ * Currently supports CDCs from IEC 61850-7-3:2010 (Edition 2)
+ *
+ * @{
+ */
+
+/**
+ * \brief optional parts of CDCs
+ */
+#define CDC_OPTION_PICS_SUBST (1 << 0)
+#define CDC_OPTION_BLK_ENA (1 << 1)
+
+/** Add d (description) data attribute */
+#define CDC_OPTION_DESC (1 << 2)
+
+/** Add dU (unicode description) data attribute */
+#define CDC_OPTION_DESC_UNICODE (1 << 3)
+
+/** Add cdcNs and cdcName required when a CDC is an extension to the standard */
+#define CDC_OPTION_AC_DLNDA (1 << 4)
+
+/** Add dataNs (data namespace) required for extended CDCs */
+#define CDC_OPTION_AC_DLN (1 << 5)
+
+/** Add the unit data attribute */
+#define CDC_OPTION_UNIT (1 << 6)
+
+#define CDC_OPTION_FROZEN_VALUE (1 << 7)
+
+#define CDC_OPTION_ADDR (1 << 8)
+#define CDC_OPTION_ADDINFO (1 << 9)
+
+#define CDC_OPTION_INST_MAG (1 << 10)
+#define CDC_OPTION_RANGE (1 << 11)
+
+#define CDC_OPTION_UNIT_MULTIPLIER (1 << 12)
+
+#define CDC_OPTION_AC_SCAV (1 << 13)
+
+#define CDC_OPTION_MIN (1 << 14)
+#define CDC_OPTION_MAX (1 << 15)
+
+#define CDC_OPTION_AC_CLC_O (1 << 16)
+
+#define CDC_OPTION_RANGE_ANG (1 << 17)
+
+#define CDC_OPTION_PHASE_A (1 << 18)
+#define CDC_OPTION_PHASE_B (1 << 19)
+#define CDC_OPTION_PHASE_C (1 << 20)
+
+#define CDC_OPTION_PHASE_NEUT (1 << 21)
+
+#define CDC_OPTION_PHASES_ABC (CDC_OPTION_PHASE_A | CDC_OPTION_PHASE_B | CDC_OPTION_PHASE_C)
+
+#define CDC_OPTION_PHASES_ALL (CDC_OPTION_PHASE_A | CDC_OPTION_PHASE_B | CDC_OPTION_PHASE_C | CDC_OPTION_PHASE_NEUT)
+
+#define CDC_OPTION_STEP_SIZE (1 << 22)
+
+#define CDC_OPTION_ANGLE_REF (1 << 23)
+
+/** Options that are only valid for DPL CDC */
+#define CDC_OPTION_DPL_HWREV (1 << 17)
+#define CDC_OPTION_DPL_SWREV (1 << 18)
+#define CDC_OPTION_DPL_SERNUM (1 << 19)
+#define CDC_OPTION_DPL_MODEL (1 << 20)
+#define CDC_OPTION_DPL_LOCATION (1 << 21)
+
+/** Add mandatory data attributes for LLN0 (e.g. LBL configRef) */
+#define CDC_OPTION_AC_LN0_M (1 << 24)
+#define CDC_OPTION_AC_LN0_EX (1 << 25)
+#define CDC_OPTION_AC_DLD_M (1 << 26)
+
+/**
+ * \brief Control model types
+ */
+#define CDC_CTL_MODEL_NONE 0
+#define CDC_CTL_MODEL_DIRECT_NORMAL 1
+#define CDC_CTL_MODEL_SBO_NORMAL 2
+#define CDC_CTL_MODEL_DIRECT_ENHANCED 3
+#define CDC_CTL_MODEL_SBO_ENHANCED 4
+
+#define CDC_CTL_MODEL_HAS_CANCEL (1 << 4)
+#define CDC_CTL_MODEL_IS_TIME_ACTIVATED (1 << 5)
+
+#define CDC_CTL_OPTION_ORIGIN (1 << 6)
+#define CDC_CTL_OPTION_CTL_NUM (1 << 7)
+#define CDC_CTL_OPTION_ST_SELD (1 << 8)
+#define CDC_CTL_OPTION_OP_RCVD (1 << 9)
+#define CDC_CTL_OPTION_OP_OK (1 << 10)
+#define CDC_CTL_OPTION_T_OP_OK (1 << 11)
+#define CDC_CTL_OPTION_SBO_TIMEOUT (1 << 12)
+#define CDC_CTL_OPTION_SBO_CLASS (1 << 13)
+#define CDC_CTL_OPTION_OPER_TIMEOUT (1 << 14)
+
+/****************************************************
+ * Constructed Attribute Classes (CAC)
+ ***************************************************/
+
+LIB61850_API DataAttribute*
+CAC_AnalogueValue_create(const char* name, ModelNode* parent, FunctionalConstraint fc, uint8_t triggerOptions,
+        bool isIntegerNotFloat);
+
+
+/**
+ * \brief create a ValWithTrans constructed data attribute
+ *
+ * \param hasTransInd
+ */
+LIB61850_API DataAttribute*
+CAC_ValWithTrans_create(const char* name, ModelNode* parent, FunctionalConstraint fc, uint8_t triggerOptions, bool hasTransientIndicator);
+
+
+/**
+ * CDC_OPTION_AC_CLC_O
+ */
+LIB61850_API DataAttribute*
+CAC_Vector_create(const char* name, ModelNode* parent, uint32_t options, FunctionalConstraint fc, uint8_t triggerOptions);
+
+LIB61850_API DataAttribute*
+CAC_Point_create(const char* name, ModelNode* parent, FunctionalConstraint fc, uint8_t triggerOptions, bool hasZVal);
+
+LIB61850_API DataAttribute*
+CAC_ScaledValueConfig_create(const char* name, ModelNode* parent);
+
+LIB61850_API DataAttribute*
+CAC_Unit_create(const char* name, ModelNode* parent, bool hasMagnitude);
+
+LIB61850_API DataAttribute*
+CDA_OperBoolean(ModelNode* parent, bool isTImeActivated);
+
+/****************************************************
+ * Common Data Classes (CDC)
+ ***************************************************/
+
+LIB61850_API DataObject*
+CDC_SPS_create(const char* dataObjectName, ModelNode* parent, uint32_t options);
+
+LIB61850_API DataObject*
+CDC_DPS_create(const char* dataObjectName, ModelNode* parent, uint32_t options);
+
+LIB61850_API DataObject*
+CDC_INS_create(const char* dataObjectName, ModelNode* parent, uint32_t options);
+
+LIB61850_API DataObject*
+CDC_ENS_create(const char* dataObjectName, ModelNode* parent, uint32_t options);
+
+LIB61850_API DataObject*
+CDC_BCR_create(const char* dataObjectName, ModelNode* parent, uint32_t options);
+
+LIB61850_API DataObject*
+CDC_VSS_create(const char* dataObjectName, ModelNode* parent, uint32_t options);
+
+/**
+ * \brief create a new SEC (Security violation) CDC instance (data object)
+ *
+ * Allowed parent types are LogicalNode and DataObject.
+ *
+ * options:
+ *   standard (include standard optional elements like extension namespaces and descriptions (d, dU).
+ *
+ *   CDC_OPTION_ADDR (address of the client causing the security violation)
+ *   CDC_OPTION_ADDINFO (additional info text)
+ *
+ * \param dataObjectName the name of the new object
+ * \param parent the parent of the new data object (either a LogicalNode or another DataObject)
+ * \param options bit mask to encode required optional elements
+ */
+LIB61850_API DataObject*
+CDC_SEC_create(const char* dataObjectName, ModelNode* parent, uint32_t options);
+
+/**
+ * \brief create a new MV (Measured value) CDC instance (data object)
+ *
+ * Allowed parent types are LogicalNode and DataObject.
+ *
+ * possible options:
+ *   CDC_OPTION_INST_MAG
+ *   CDC_OPTION_RANGE
+ *   CDC_OPTION_PICS_SUBST
+ *   standard (include standard optional elements like extension namespaces and descriptions (d, dU).
+ *
+ * \param dataObjectName the name of the new object
+ * \param parent the parent of the new data object (either a LogicalNode or another DataObject)
+ * \param options bit mask to encode required optional elements
+ * \param isIntegerNotFloat if true the AnalogueValue instance have integer instead of float
+ *
+ */
+LIB61850_API DataObject*
+CDC_MV_create(const char* dataObjectName, ModelNode* parent, uint32_t options, bool isIntegerNotFloat);
+
+/**
+ * CDC_OPTION_INST_MAG
+ * CDC_OPTION_RANGE
+ */
+LIB61850_API DataObject*
+CDC_CMV_create(const char* dataObjectName, ModelNode* parent, uint32_t options);
+
+/**
+ * \brief create a new SAV (Sampled analog value) CDC instance (data object)
+ *
+ * Allowed parent types are LogicalNode and DataObject.
+ *
+ * possible options:
+ *   CDC_OPTION_UNIT
+ *   CDC_OPTION_AC_SCAV
+ *   CDC_OPTION_MIN
+ *   CDC_OPTION_MAX
+ *   standard (include standard optional elements like extension namespaces and descriptions (d, dU).
+ *
+ * \param dataObjectName the name of the new object
+ * \param parent the parent of the new data object (either a LogicalNode or another DataObject)
+ * \param options bit mask to encode required optional elements
+ * \param isIntegerNotFloat if true the AnalogueValue instance have integer instead of float
+ *
+ */
+LIB61850_API DataObject*
+CDC_SAV_create(const char* dataObjectName, ModelNode* parent, uint32_t options, bool isIntegerNotFloat);
+
+/**
+ * \brief create a new LPL (Logical node name plate) CDC instance (data object)
+ *
+ * Allowed parent type is LogicalNode
+ *
+ * possible options:
+ *   CDC_OPTION_AC_LN0_M (includes "configRev")
+ *   CDC_OPTION_AC_LN0_EX (includes "ldNs")
+ *   CDC_OPTION_AC_DLD_M (includes "lnNs")
+ *   standard options:
+ *   CDC_OPTION_DESC (includes "d")
+ *   CDC_OPTION_DESC_UNICODE (include "du")
+ *   CDC_OPTION_AC_DLNDA (include "cdcNs" and "cdcName")
+ *   CDC_OPTION_AC_DLN (includes "dataNs")
+ *
+ * \param dataObjectName the name of the new object
+ * \param parent the parent of the new data object (either a LogicalNode or another DataObject)
+ * \param options bit mask to encode required optional elements
+ *
+ * \return new DataObject instance
+ */
+LIB61850_API DataObject*
+CDC_LPL_create(const char* dataObjectName, ModelNode* parent, uint32_t options);
+
+/**
+ * \brief create a new DPL (Device name plate) CDC instance (data object)
+ *
+ * Allowed parent type is LogicalNode
+ *
+ * possible options:
+ *   CDC_OPTION_DPL_HWREV (includes "hwRev")
+ *   CDC_OPTION_DPL_SWREV (includes "swRev")
+ *   CDC_OPTION_DPL_SERNUM (includes "serNum")
+ *   CDC_OPTION_DPL_MODEL (includes "model")
+ *   CDC_OPTION_DPL_LOCATION (includes "location")
+ *   standard options:
+ *   CDC_OPTION_AC_DLNDA (include "cdcNs" and "cdcName")
+ *   CDC_OPTION_AC_DLN (includes "dataNs")
+ *
+ * \param dataObjectName the name of the new object
+ * \param parent the parent of the new data object (either a LogicalNode or another DataObject)
+ * \param options bit mask to encode required optional elements
+ *
+ * \return new DataObject instance
+ */
+LIB61850_API DataObject*
+CDC_DPL_create(const char* dataObjectName, ModelNode* parent, uint32_t options);
+
+LIB61850_API DataObject*
+CDC_HST_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint16_t maxPts);
+
+/**
+ * \brief Directional protection activation information (ACD)
+ *
+ * Allowed parent types are LogicalNode and DataObject.
+ *
+ * possible options:
+ *   CDC_OPTION_PHASE_A
+ *   CDC_OPTION_PHASE_B
+ *   CDC_OPTION_PHASE_C
+ *   CDC_OPTION_PHASE_NEUT
+ *   CDC_OPTION_PHASES_ABC
+ *   CDC_OPTION_PHASES_ALL
+ *   standard (include standard optional elements like extension namespaces and descriptions (d, dU).
+ *
+ * \param dataObjectName the name of the new object
+ * \param parent the parent of the new data object (either a LogicalNode or another DataObject)
+ * \param options bit mask to encode required optional elements
+ */
+LIB61850_API DataObject*
+CDC_ACD_create(const char* dataObjectName, ModelNode* parent, uint32_t options);
+
+/**
+ * \brief Protection activation information (ACT)
+ */
+LIB61850_API DataObject*
+CDC_ACT_create(const char* dataObjectName, ModelNode* parent, uint32_t options);
+
+/**
+ * \brief Single point setting (SPG)
+ *
+ * \param dataObjectName the name of the new object
+ * \param parent the parent of the new data object (either a LogicalNode or another DataObject)
+ * \param options bit mask to encode required optional elements
+ */
+LIB61850_API DataObject*
+CDC_SPG_create(const char* dataObjectName, ModelNode* parent, uint32_t options);
+
+/**
+ * \brief Visible string setting (VSG)
+ *
+ * \param dataObjectName the name of the new object
+ * \param parent the parent of the new data object (either a LogicalNode or another DataObject)
+ * \param options bit mask to encode required optional elements
+ */
+LIB61850_API DataObject*
+CDC_VSG_create(const char* dataObjectName, ModelNode* parent, uint32_t options);
+
+/**
+ * \brief Enumerated status setting (ENG)
+ *
+ * \param dataObjectName the name of the new object
+ * \param parent the parent of the new data object (either a LogicalNode or another DataObject)
+ * \param options bit mask to encode required optional elements
+ */
+LIB61850_API DataObject*
+CDC_ENG_create(const char* dataObjectName, ModelNode* parent, uint32_t options);
+
+/**
+ * \brief Integer status setting (ING)
+ *
+ * possible options:
+ *   CDC_OPTION_UNIT
+ *   CDC_OPTION_MIN
+ *   CDC_OPTION_MAX
+ *   CDC_OPTION_STEP_SIZE
+ *   standard (include standard optional elements like extension namespaces and descriptions (d, dU).
+ *
+ */
+LIB61850_API DataObject*
+CDC_ING_create(const char* dataObjectName, ModelNode* parent, uint32_t options);
+
+/**
+ * \brief Analogue Setting (ASG)
+ *
+ * possible options:
+ *   CDC_OPTION_UNIT
+ *   CDC_OPTION_MIN
+ *   CDC_OPTION_MAX
+ *   CDC_OPTION_STEP_SIZE
+ *   standard (include standard optional elements like extension namespaces and descriptions (d, dU).
+ *
+ */
+LIB61850_API DataObject*
+CDC_ASG_create(const char* dataObjectName, ModelNode* parent, uint32_t options, bool isIntegerNotFloat);
+
+/**
+ * \brief Phase to ground/neutral related measured values of a three-phase system (WYE)
+ *
+ * possible options:
+ *   CDC_OPTION_ANGLE_REF
+ */
+LIB61850_API DataObject*
+CDC_WYE_create(const char* dataObjectName, ModelNode* parent, uint32_t options);
+
+/**
+ * \brief Phase to phase related measured values of a three-phase system (DEL)
+ *
+ * possible options:
+ *   CDC_OPTION_ANGLE_REF
+ */
+LIB61850_API DataObject*
+CDC_DEL_create(const char* dataObjectName, ModelNode* parent, uint32_t options);
+
+/***************************
+ * Controls
+ ***************************/
+
+/**
+ * \brief Controllable single point (SPC)
+ *
+ * \param controlOptions specify which control model to set as default and other control related options
+ */
+LIB61850_API DataObject*
+CDC_SPC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions);
+
+/**
+ * \brief Controllable double point (DPC)
+ *
+ * CDC_OPTION_IS_TIME_ACTICATED
+ *
+ * substitution options
+ * CDC_OPTION_BLK_ENA
+ * standard description and namespace options
+ *
+ * \param dataObjectName the name of the new object
+ * \param parent the parent of the new data object (either a LogicalNode or another DataObject)
+ * \param options bit mask to encode required optional elements
+ * \param defaultControlModel specify which control model to set as default.
+ *
+ */
+LIB61850_API DataObject*
+CDC_DPC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions);
+
+/**
+ * \brief Controllable integer status (INC)
+ *
+ * CDC_OPTION_IS_TIME_ACTICATED
+ *
+ * CDC_OPTION_MIN
+ * CDC_OPTION_MAX
+ * CDC_OPTION_STEP_SIZE
+ *
+ * substitution options
+ * CDC_OPTION_BLK_ENA
+ * standard description and namespace options
+ *
+ * \param dataObjectName the name of the new object
+ * \param parent the parent of the new data object (either a LogicalNode or another DataObject)
+ * \param options bit mask to encode required optional elements
+ * \param defaultControlModel specify which control model to set as default.
+ *
+ */
+LIB61850_API DataObject*
+CDC_INC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions);
+
+/**
+ * \brief Controllable enumerated status (ENC)
+ *
+ * CDC_OPTION_IS_TIME_ACTICATED
+ *
+ * substitution options
+ * CDC_OPTION_BLK_ENA
+ * standard description and namespace options
+ *
+ * \param dataObjectName the name of the new object
+ * \param parent the parent of the new data object (either a LogicalNode or another DataObject)
+ * \param options bit mask to encode required optional elements
+ * \param defaultControlModel specify which control model to set as default.
+ *
+ */
+LIB61850_API DataObject*
+CDC_ENC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions);
+
+/**
+ * \brief Controllable enumerated status (ENC)
+ *
+ * CDC_OPTION_IS_TIME_ACTICATED
+ *
+ * substitution options
+ * CDC_OPTION_BLK_ENA
+ * standard description and namespace options
+ *
+ * \param dataObjectName the name of the new object
+ * \param parent the parent of the new data object (either a LogicalNode or another DataObject)
+ * \param options bit mask to encode required optional elements
+ * \param controlOptions specify which control model to set as default and other control specific options
+ * \param hasTransientIndicator specifies if the step position information contains the transient indicator
+ *
+ */
+LIB61850_API DataObject*
+CDC_BSC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool hasTransientIndicator);
+
+/**
+ * \brief Integer controlled step position information (ISC)
+ *
+ * CDC_OPTION_IS_TIME_ACTICATED
+ *
+ * substitution options
+ * CDC_OPTION_BLK_ENA
+ * standard description and namespace options
+ *
+ * \param dataObjectName the name of the new object
+ * \param parent the parent of the new data object (either a LogicalNode or another DataObject)
+ * \param options bit mask to encode required optional elements
+ * \param controlOptions specify which control model to set as default and other control specific options
+ * \param hasTransientIndicator specifies if the step position information contains the transient indicator
+ *
+ */
+LIB61850_API DataObject*
+CDC_ISC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool hasTransientIndicator);
+
+/**
+ * \brief Controllable analogue process value (APC)
+ *
+ * CDC_OPTION_IS_TIME_ACTICATED
+ *
+ * CDC_OPTION_MIN
+ * CDC_OPTION_MAX
+ *
+ * substitution options
+ * CDC_OPTION_BLK_ENA
+ * standard description and namespace options
+ *
+ * \param dataObjectName the name of the new object
+ * \param parent the parent of the new data object (either a LogicalNode or another DataObject)
+ * \param options bit mask to encode required optional elements
+ * \param controlOptions specify which control model to set as default and other control specific options
+ * \param isIntegerNotFloat
+ */
+LIB61850_API DataObject*
+CDC_APC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool isIntegerNotFloat);
+
+/**
+ * \brief Binary controlled ananlogue process value (BAC)
+ *
+ * CDC_OPTION_IS_TIME_ACTICATED
+ *
+ * CDC_OPTION_MIN
+ * CDC_OPTION_MAX
+ * CDC_OPTION_STEP_SIZE
+ *
+ * substitution options
+ * CDC_OPTION_BLK_ENA
+ * standard description and namespace options
+ *
+ * \param dataObjectName the name of the new object
+ * \param parent the parent of the new data object (either a LogicalNode or another DataObject)
+ * \param options bit mask to encode required optional elements
+ * \param controlOptions specify which control model to set as default and other control specific options
+ * \param isIntegerNotFloat
+ */
+LIB61850_API DataObject*
+CDC_BAC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool isIntegerNotFloat);
+
+/** Minimum measured value */
+#define CDC_OPTION_61400_MIN_MX_VAL (1 << 10)
+
+/** Maximum measured value */
+#define CDC_OPTION_61400_MAX_MX_VAL (1 << 11)
+
+/** Total average value of data */
+#define CDC_OPTION_61400_TOT_AV_VAL (1 << 12)
+
+/** Standard deviation of data */
+#define CDC_OPTION_61400_SDV_VAL (1 << 13)
+
+/** Rate of increase */
+#define CDC_OPTION_61400_INC_RATE (1 << 14)
+
+/** Rate of decrease */
+#define CDC_OPTION_61400_DEC_RATE (1 << 15)
+
+/** Setpoint or parameter access level (low/medium/high) */
+#define CDC_OPTION_61400_SP_ACS (1 << 16)
+
+/** Time periodical reset (hourly/daily/weekly/monthly) */
+#define CDC_OPTION_61400_CHA_PER_RS (1 << 17)
+
+/** Command access level */
+#define CDC_OPTION_61400_CM_ACS (1 << 18)
+
+/** Total time duration of a state */
+#define CDC_OPTION_61400_TM_TOT (1 << 19)
+
+/** Daily counting data */
+#define CDC_OPTION_61400_COUNTING_DAILY (1 << 20)
+
+/** Monthly counting data */
+#define CDC_OPTION_61400_COUNTING_MONTHLY (1 << 21)
+
+/** Yearly counting data */
+#define CDC_OPTION_61400_COUNTING_YEARLY (1 << 22)
+
+/** Total counting data */
+#define CDC_OPTION_61400_COUNTING_TOTAL (1 << 23)
+
+/** All counting data */
+#define CDC_OPTION_61400_COUNTING_ALL (CDC_OPTION_61400_COUNTING_DAILY | CDC_OPTION_61400_COUNTING_MONTHLY | CDC_OPTION_61400_COUNTING_YEARLY | CDC_OPTION_61400_COUNTING_TOTAL)
+
+LIB61850_API DataObject*
+CDC_SPV_create(const char* dataObjectName, ModelNode* parent,
+        uint32_t options,
+        uint32_t controlOptions,
+        uint32_t wpOptions,
+        bool hasChaManRs);
+
+LIB61850_API DataObject*
+CDC_STV_create(const char* dataObjectName, ModelNode* parent,
+        uint32_t options,
+        uint32_t controlOptions,
+        uint32_t wpOptions,
+        bool hasOldStatus);
+
+LIB61850_API DataObject*
+CDC_CMD_create(const char* dataObjectName, ModelNode* parent,
+        uint32_t options,
+        uint32_t controlOptions,
+        uint32_t wpOptions,
+        bool hasOldStatus,
+        bool hasCmTm,
+        bool hasCmCt);
+
+LIB61850_API DataObject*
+CDC_ALM_create(const char* dataObjectName, ModelNode* parent,
+        uint32_t options,
+        uint32_t controlOptions,
+        uint32_t wpOptions,
+        bool hasOldStatus);
+
+LIB61850_API DataObject*
+CDC_CTE_create(const char* dataObjectName, ModelNode* parent,
+        uint32_t options,
+        uint32_t controlOptions,
+        uint32_t wpOptions,
+        bool hasHisRs);
+
+LIB61850_API DataObject*
+CDC_TMS_create(const char* dataObjectName, ModelNode* parent,
+        uint32_t options,
+        uint32_t controlOptions,
+        uint32_t wpOptions,
+        bool hasHisRs);
+
+/**@}*/
+
+/**@}*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CDC_H_ */

+ 3158 - 0
library/include/libiec61850/iec61850_client.h

@@ -0,0 +1,3158 @@
+/*
+ *  iec61850_client.h
+ *
+ *  Copyright 2013-2023 Michael Zillgith
+ *
+ *  This file is part of libIEC61850.
+ *
+ *  libIEC61850 is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  libIEC61850 is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with libIEC61850.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef IEC61850_CLIENT_H_
+#define IEC61850_CLIENT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "libiec61850_common_api.h"
+#include "iec61850_common.h"
+#include "mms_value.h"
+#include "mms_client_connection.h"
+#include "linked_list.h"
+
+/**
+ *  * \defgroup iec61850_client_api_group IEC 61850/MMS client API
+ */
+/**@{*/
+
+/** an opaque handle to the instance data of a ClientDataSet object */
+typedef struct sClientDataSet* ClientDataSet;
+
+/** an opaque handle to the instance data of a ClientReport object */
+typedef struct sClientReport* ClientReport;
+
+/** an opaque handle to the instance data of a ClientReportControlBlock object */
+typedef struct sClientReportControlBlock* ClientReportControlBlock;
+
+/** an opaque handle to the instance data of a ClientGooseControlBlock object */
+typedef struct sClientGooseControlBlock* ClientGooseControlBlock;
+
+/**
+ * @defgroup IEC61850_CLIENT_GENERAL General client side connection handling functions and data types
+ *
+ * @{
+ */
+
+/** An opaque handle to the instance data of the IedConnection object */
+typedef struct sIedConnection* IedConnection;
+
+/** Detailed description of the last application error of the client connection instance */
+typedef struct
+{
+    int ctlNum;
+    ControlLastApplError error;
+    ControlAddCause addCause;
+} LastApplError;
+
+/** Connection state of the IedConnection instance - either closed(idle), connecting, connected, or closing) */
+typedef enum
+{
+    IED_STATE_CLOSED = 0,
+    IED_STATE_CONNECTING,
+    IED_STATE_CONNECTED,
+    IED_STATE_CLOSING
+} IedConnectionState;
+
+/** used to describe the error reason for most client side service functions */
+typedef enum {
+    /* general errors */
+
+    /** No error occurred - service request has been successful */
+    IED_ERROR_OK = 0,
+
+    /** The service request can not be executed because the client is not yet connected */
+    IED_ERROR_NOT_CONNECTED = 1,
+
+    /** Connect service not execute because the client is already connected */
+    IED_ERROR_ALREADY_CONNECTED = 2,
+
+    /** The service request can not be executed caused by a loss of connection */
+    IED_ERROR_CONNECTION_LOST = 3,
+
+    /** The service or some given parameters are not supported by the client stack or by the server */
+    IED_ERROR_SERVICE_NOT_SUPPORTED = 4,
+
+    /** Connection rejected by server */
+    IED_ERROR_CONNECTION_REJECTED = 5,
+
+    /** Cannot send request because outstanding call limit is reached */
+    IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED = 6,
+
+    /* client side errors */
+
+    /** API function has been called with an invalid argument */
+    IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT = 10,
+
+    IED_ERROR_ENABLE_REPORT_FAILED_DATASET_MISMATCH = 11,
+
+    /** The object provided object reference is invalid (there is a syntactical error). */
+    IED_ERROR_OBJECT_REFERENCE_INVALID = 12,
+
+    /** Received object is of unexpected type */
+    IED_ERROR_UNEXPECTED_VALUE_RECEIVED = 13,
+
+    /* service error - error reported by server */
+
+    /** The communication to the server failed with a timeout */
+    IED_ERROR_TIMEOUT = 20,
+
+    /** The server rejected the access to the requested object/service due to access control */
+    IED_ERROR_ACCESS_DENIED = 21,
+
+    /** The server reported that the requested object does not exist (returned by server) */
+    IED_ERROR_OBJECT_DOES_NOT_EXIST = 22,
+
+    /** The server reported that the requested object already exists */
+    IED_ERROR_OBJECT_EXISTS = 23,
+
+    /** The server does not support the requested access method (returned by server) */
+    IED_ERROR_OBJECT_ACCESS_UNSUPPORTED = 24,
+
+    /** The server expected an object of another type (returned by server) */
+    IED_ERROR_TYPE_INCONSISTENT = 25,
+
+    /** The object or service is temporarily unavailable (returned by server) */
+    IED_ERROR_TEMPORARILY_UNAVAILABLE = 26,
+
+    /** The specified object is not defined in the server (returned by server) */
+    IED_ERROR_OBJECT_UNDEFINED = 27,
+
+    /** The specified address is invalid (returned by server) */
+    IED_ERROR_INVALID_ADDRESS = 28,
+
+    /** Service failed due to a hardware fault (returned by server) */
+    IED_ERROR_HARDWARE_FAULT = 29,
+
+    /** The requested data type is not supported by the server (returned by server) */
+    IED_ERROR_TYPE_UNSUPPORTED = 30,
+
+    /** The provided attributes are inconsistent (returned by server) */
+    IED_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT = 31,
+
+    /** The provided object value is invalid (returned by server) */
+    IED_ERROR_OBJECT_VALUE_INVALID = 32,
+
+    /** The object is invalidated (returned by server) */
+    IED_ERROR_OBJECT_INVALIDATED = 33,
+
+    /** Received an invalid response message from the server */
+    IED_ERROR_MALFORMED_MESSAGE = 34,
+
+    /** Service was not executed because required resource is still in use */
+    IED_ERROR_OBJECT_CONSTRAINT_CONFLICT = 35,
+
+    /** Service not implemented */
+    IED_ERROR_SERVICE_NOT_IMPLEMENTED = 98,
+
+    /** unknown error */
+    IED_ERROR_UNKNOWN = 99
+} IedClientError;
+
+/**
+ * \brief Convert error value to string
+ *
+ * \return string constant representing the error
+ */
+LIB61850_API const char*
+IedClientError_toString(IedClientError err);
+
+/**************************************************
+ * Connection creation and destruction
+ **************************************************/
+
+/**
+ * \brief create a new IedConnection instance
+ *
+ * This function creates a new IedConnection instance that is used to handle a connection to an IED.
+ * It allocated all required resources. The new connection is in the "CLOSED" state. Before it can be used
+ * the connect method has to be called. The connection will be in non-TLS and thread mode.
+ *
+ * \return the new IedConnection instance
+ */
+LIB61850_API IedConnection
+IedConnection_create(void);
+
+/**
+ * \brief create a new IedConnection instance (extended version)
+ *
+ * This function creates a new IedConnection instance that is used to handle a connection to an IED.
+ * It allocated all required resources. The new connection is in the "CLOSED" state. Before it can be used
+ * the \ref IedConnection_connect or \ref IedConnection_connectAsync method has to be called.
+ * The connection will use TLS when a TLSConfiguration object is provided. The useThread is false the
+ * IedConnection is in non-thread mode and the IedConnection_tick function has to be called periodically to
+ * receive messages and perform the house-keeping tasks.
+ *
+ * \param tlsConfig the TLS configuration to be used, or NULL for non-TLS connection
+ * \param useThreads when true, the IedConnection is in thread mode
+ *
+ * \return the new IedConnection instance
+ */
+LIB61850_API IedConnection
+IedConnection_createEx(TLSConfiguration tlsConfig, bool useThreads);
+
+/**
+ * \brief create a new IedConnection instance that has support for TLS
+ *
+ * This function creates a new IedConnection instance that is used to handle a connection to an IED.
+ * It allocated all required resources. The new connection is in the "CLOSED" state. Before it can be used
+ * the \ref IedConnection_connect or \ref IedConnection_connectAsync method has to be called.
+ * The connection will use TLS when a TLSConfiguration object is provided. The connection will be in thread mode.
+ *
+ * \deprecated Use \ref IedConnection_createEx instead
+ *
+ * \param tlsConfig the TLS configuration to be used
+ *
+ * \return the new IedConnection instance
+ */
+LIB61850_API IedConnection
+IedConnection_createWithTlsSupport(TLSConfiguration tlsConfig);
+
+/**
+ * \brief destroy an IedConnection instance.
+ *
+ * The connection will be closed if it is in "connected" state. All allocated resources of the connection
+ * will be freed.
+ *
+ * \param self the connection object
+ */
+LIB61850_API void
+IedConnection_destroy(IedConnection self);
+
+/**
+* \brief Set the local IP address and port to be used by the client
+*
+* NOTE: This function is optional. When not used the OS decides what IP address and TCP port to use.
+*
+* \param self IedConnection instance
+* \param localIpAddress the local IP address or hostname as C string
+* \param localPort the local TCP port to use. When < 1 the OS will chose the TCP port to use.
+*/
+LIB61850_API void
+IedConnection_setLocalAddress(IedConnection self, const char* localIpAddress, int localPort);
+
+/**
+ * \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
+ */
+LIB61850_API void
+IedConnection_setConnectTimeout(IedConnection self, uint32_t timeoutInMs);
+
+/**
+ * \brief Set the maximum number outstanding calls allowed for this connection
+ *
+ * \param self the connection object
+ * \param calling the maximum outstanding calls allowed by the caller (client)
+ * \param called the maximum outstanding calls allowed by the called endpoint (server)
+ */
+LIB61850_API void
+IedConnection_setMaxOutstandingCalls(IedConnection self, int calling, int called);
+
+/**
+ * \brief set the request timeout in ms
+ *
+ * Set the request timeout for this connection. You can call this function any time to adjust
+ * timeout behavior.
+ *
+ * \param self the connection object
+ * \param timoutInMs the connection timeout in ms
+ */
+LIB61850_API void
+IedConnection_setRequestTimeout(IedConnection self, uint32_t timeoutInMs);
+
+/**
+ * \brief get the request timeout in ms for this connection
+ *
+ * \param self the connection object
+ *
+ * \return request timeout in milliseconds
+ */
+LIB61850_API uint32_t
+IedConnection_getRequestTimeout(IedConnection self);
+
+/**
+ * \brief Set the time quality for all timestamps generated by this IedConnection instance
+ *
+ * \param self the connection object
+ * \param leapSecondKnown set/unset leap seconds known flag
+ * \param clockFailure set/unset clock failure flag
+ * \param clockNotSynchronized set/unset clock not synchronized flag
+ * \param subsecondPrecision set the subsecond precision (number of significant bits of the fractionOfSecond part of the time stamp)
+ */
+LIB61850_API void
+IedConnection_setTimeQuality(IedConnection self, bool leapSecondKnown, bool clockFailure, bool clockNotSynchronized, int subsecondPrecision);
+
+/**
+ * \brief Perform MMS message handling and house-keeping tasks (for non-thread mode only)
+ *
+ * This function has to be called periodically by the user application in non-thread mode. The return
+ * value helps to decide when the stack has nothing to do and other tasks can be executed.
+ *
+ * NOTE: When using non-thread mode you should NOT use the synchronous (blocking) API functions. The
+ * synchronous functions will block forever when IedConnection_tick is not called in a separate thread.
+ *
+ * \return true when connection is currently waiting and calling thread can be suspended, false means
+ *         connection is busy and the tick function should be called again as soon as possible.
+ */
+LIB61850_API bool
+IedConnection_tick(IedConnection self);
+
+/**
+ * \brief Generic serivce callback handler
+ *
+ * NOTE: This callback handler is used by several asynchronous service functions that require
+ * only a simple feedback in form of a success (IED_ERROR_OK) or failure (other \ref err value).
+ *
+ * \param invokeId the invoke ID used by the related service request
+ * \param parameter user provided parameter
+ * \param err the result code. IED_ERROR_OK indicates success.
+ */
+typedef void
+(*IedConnection_GenericServiceHandler) (uint32_t invokeId, void* parameter, IedClientError err);
+
+/**************************************************
+ * Association service
+ **************************************************/
+
+/**
+ * \brief Connect to a server
+ *
+ * NOTE: Function will block until connection is up or timeout happened.
+ *
+ * \param self the connection object
+ * \param error the error code if an error occurs
+ * \param hostname the host name or IP address of the server to connect to
+ * \param tcpPort the TCP port number of the server to connect to
+ */
+LIB61850_API void
+IedConnection_connect(IedConnection self, IedClientError* error, const char* hostname, int tcpPort);
+
+/**
+ * \brief Asynchronously connect to a server
+ *
+ * The function will return immediately. No error doesn't indicate that the
+ * connection is established. The current connection state has to be tracked
+ * by polling the \ref IedConnection_getState function or by using
+ * \ref IedConnection_StateChangedHandler
+ *
+ * \param self the connection object
+ * \param error the error code if an error occurs
+ * \param hostname the host name or IP address of the server to connect to
+ * \param tcpPort the TCP port number of the server to connect to
+ */
+LIB61850_API void
+IedConnection_connectAsync(IedConnection self, IedClientError* error, const char* hostname, int tcpPort);
+
+/**
+ * \brief Abort the connection
+ *
+ * This will close the MMS association by sending an ACSE abort message to the server.
+ * After sending the abort message the connection is closed immediately.
+ * The client can assume the connection to be closed when the function returns and the
+ * destroy method can be called. If the connection is not in "connected" state an
+ * IED_ERROR_NOT_CONNECTED error will be reported.
+ *
+ * \param self the connection object
+ * \param error the error code if an error occurs
+ */
+LIB61850_API void
+IedConnection_abort(IedConnection self, IedClientError* error);
+
+/**
+ * \brief Asynchronously abort the connection
+ *
+ * This will close the MMS association by sending an ACSE abort message to the server.
+ * After sending the abort message the connection is closed immediately.
+ * If the connection is not in "connected" state an IED_ERROR_NOT_CONNECTED error will be reported.
+ *
+ * NOTE: This function works asynchronously. The IedConnection object should not be destroyed before the
+ * connection state changes to IED_STATE_CLOSED.
+ *
+ * \param self the connection object
+ * \param error the error code if an error occurs
+ */
+LIB61850_API void
+IedConnection_abortAsync(IedConnection self, IedClientError* error);
+
+/**
+ * \brief Release the connection
+ *
+ * This will release the MMS association by sending an MMS conclude message to the server.
+ * The client can NOT assume the connection to be closed when the function returns, It can
+ * also fail if the server returns with a negative response. To be sure that the connection
+ * will be close the close or abort methods should be used. If the connection is not in "connected" state an
+ * IED_ERROR_NOT_CONNECTED error will be reported.
+ *
+ * \param self the connection object
+ * \param error the error code if an error occurs
+ */
+LIB61850_API void
+IedConnection_release(IedConnection self, IedClientError* error);
+
+/**
+ * \brief Asynchronously release the connection
+ *
+ * This will release the MMS association by sending an MMS conclude message to the server.
+ * The client can NOT assume the connection to be closed when the function returns, It can
+ * also fail if the server returns with a negative response. To be sure that the connection
+ * will be close the close or abort methods should be used. If the connection is not in "connected" state an
+ * IED_ERROR_NOT_CONNECTED error will be reported.
+ *
+ * \param self the connection object
+ * \param error the error code if an error occurs
+ */
+LIB61850_API void
+IedConnection_releaseAsync(IedConnection self, IedClientError* error);
+
+/**
+ * \brief Close the connection
+ *
+ * This will close the MMS association and the underlying TCP connection.
+ *
+ * \param self the connection object
+ */
+LIB61850_API void
+IedConnection_close(IedConnection self);
+
+/**
+ * \brief return the state of the connection.
+ *
+ * This function can be used to determine if the connection is established or closed.
+ *
+ * \param self the connection object
+ *
+ * \return the connection state
+ */
+LIB61850_API IedConnectionState
+IedConnection_getState(IedConnection self);
+
+/**
+ * \brief Access to last application error received by the client connection
+ *
+ * \param self the connection object
+ *
+ * \return the LastApplError value
+ */
+LIB61850_API LastApplError
+IedConnection_getLastApplError(IedConnection self);
+
+/**
+ * \brief Callback handler that is invoked when the connection is closed
+ *
+ * \deprecated Use \ref IedConnection_StateChangedHandler instead
+ *
+ * \param user provided parameter
+ * \param connection the connection object of the closed connection
+ */
+typedef void
+(*IedConnectionClosedHandler) (void* parameter, IedConnection connection);
+
+/**
+ * \brief Install a handler function that is called when the connection is lost/closed.
+ *
+ * \deprecated Use \ref IedConnection_StateChangedHandler instead
+ *
+ * \param self the connection object
+ * \param handler that callback function
+ * \param parameter the user provided parameter that is handed over to the callback function
+ */
+LIB61850_API void
+IedConnection_installConnectionClosedHandler(IedConnection self, IedConnectionClosedHandler handler,
+        void* parameter);
+
+/**
+ * \brief Callback handler that is invoked whenever the connection state (\ref IedConnectionState) changes
+ *
+ * \param user provided parameter
+ * \param connection the related connection
+ * \param newState the new state of the connection
+ */
+typedef void
+(*IedConnection_StateChangedHandler) (void* parameter, IedConnection connection, IedConnectionState newState);
+
+/**
+ * \brief Install a handler function that is called when the connection state changes
+ *
+ * \param self the connection object
+ * \param handler that callback function
+ * \param parameter the user provided parameter that is handed over to the callback function
+ */
+LIB61850_API void
+IedConnection_installStateChangedHandler(IedConnection self, IedConnection_StateChangedHandler handler, void* parameter);
+
+/**
+ * \brief get a handle to the underlying MmsConnection
+ *
+ * Get access to the underlying MmsConnection instance used by this IedConnection.
+ * This can be used to set/change specific MmsConnection parameters or to call low-level MMS services/functions.
+ *
+ * \param self the connection object
+ *
+ * \return the MmsConnection instance used by this IedConnection.
+ */
+LIB61850_API MmsConnection
+IedConnection_getMmsConnection(IedConnection self);
+
+/** @} */
+
+/**
+ * @defgroup IEC61850_CLIENT_SV Client side SV control block handling functions
+ *
+ * @{
+ */
+
+/** SV ASDU contains attribute RefrTm */
+#define IEC61850_SV_OPT_REFRESH_TIME 1
+
+/** SV ASDU contains attribute SmpSynch */
+#define IEC61850_SV_OPT_SAMPLE_SYNC 2
+
+/** SV ASDU contains attribute SmpRate */
+#define IEC61850_SV_OPT_SAMPLE_RATE 4
+
+/** SV ASDU contains attribute DatSet */
+#define IEC61850_SV_OPT_DATA_SET 8
+
+/** SV ASDU contains attribute Security */
+#define IEC61850_SV_OPT_SECURITY 16
+
+#define IEC61850_SV_SMPMOD_SAMPLES_PER_PERIOD 0
+
+#define IEC61850_SV_SMPMOD_SAMPLES_PER_SECOND 1
+
+#define IEC61850_SV_SMPMOD_SECONDS_PER_SAMPLE 2
+
+
+/** an opaque handle to the instance data of a ClientSVControlBlock object */
+typedef struct sClientSVControlBlock* ClientSVControlBlock;
+
+/**
+ * \brief Create a new ClientSVControlBlock instance
+ *
+ * This function simplifies client side access to server MSV/USV control blocks
+ * NOTE: Do not use the functions after the IedConnection object is invalidated!
+ *
+ * The access functions cause synchronous read/write calls to the server. For asynchronous
+ * access use the \ref IedConnection_readObjectAsync and \ref IedConnection_writeObjectAsync
+ * functions.
+ *
+ * \param connection the IedConnection object with a valid connection to the server.
+ * \param reference the object reference of the control block
+ *
+ * \return the new instance
+ */
+LIB61850_API ClientSVControlBlock
+ClientSVControlBlock_create(IedConnection connection, const char* reference);
+
+/**
+ * \brief Free all resources related to the ClientSVControlBlock instance.
+ *
+ * \param self the ClientSVControlBlock instance to operate on
+ */
+LIB61850_API void
+ClientSVControlBlock_destroy(ClientSVControlBlock self);
+
+/**
+ * \brief Test if this SVCB is multicast
+ *
+ * \param self the ClientSVControlBlock instance to operate on
+ *
+ * \return true if multicast SCVB, false otherwise (unicast)
+ */
+LIB61850_API bool
+ClientSVControlBlock_isMulticast(ClientSVControlBlock self);
+
+/**
+ * \brief Return the error code of the last write or write acccess to the SVCB
+ *
+ * \param self the ClientSVControlBlock instance to operate on
+ *
+ * \return the error code of the last read or write access
+ */
+LIB61850_API IedClientError
+ClientSVControlBlock_getLastComError(ClientSVControlBlock self);
+
+
+LIB61850_API bool
+ClientSVControlBlock_setSvEna(ClientSVControlBlock self, bool value);
+
+LIB61850_API bool
+ClientSVControlBlock_getSvEna(ClientSVControlBlock self);
+
+LIB61850_API bool
+ClientSVControlBlock_setResv(ClientSVControlBlock self, bool value);
+
+LIB61850_API bool
+ClientSVControlBlock_getResv(ClientSVControlBlock self);
+
+LIB61850_API char*
+ClientSVControlBlock_getMsvID(ClientSVControlBlock self);
+
+/**
+ * \brief Get the (MMS) reference to the data set
+ *
+ * NOTE: the returned string is dynamically allocated with the
+ * GLOBAL_MALLOC macro. The application is responsible to release
+ * the memory when the string is no longer needed.
+ *
+ * \param self the ClientSVControlBlock instance to operate on
+ *
+ * \return the data set reference as a NULL terminated string
+ */
+LIB61850_API char*
+ClientSVControlBlock_getDatSet(ClientSVControlBlock self);
+
+LIB61850_API uint32_t
+ClientSVControlBlock_getConfRev(ClientSVControlBlock self);
+
+LIB61850_API uint16_t
+ClientSVControlBlock_getSmpRate(ClientSVControlBlock self);
+
+
+/**
+ * \brief returns the destination address of the SV publisher
+ *
+ * \param self the ClientSVControlBlock instance to operate on
+ */
+LIB61850_API PhyComAddress
+ClientSVControlBlock_getDstAddress(ClientSVControlBlock self);
+
+/**
+ * \brief Gets the OptFlds parameter of the RCB (decides what information to include in a report)
+ *
+ * \param self the RCB instance
+ * \return bit field representing the optional fields of a report (uses flags from \ref REPORT_OPTIONS)
+ */
+LIB61850_API int
+ClientSVControlBlock_getOptFlds(ClientSVControlBlock self);
+
+/**
+ * \brief returns number of sample mode of the SV publisher
+ *
+ * \param self the ClientSVControlBlock instance to operate on
+ */
+LIB61850_API uint8_t
+ClientSVControlBlock_getSmpMod(ClientSVControlBlock self);
+
+/**
+ * \brief returns number of ASDUs included in the SV message
+ *
+ * \param self the ClientSVControlBlock instance to operate on
+ *
+ * \return the number of ASDU included in a single SV message
+ */
+LIB61850_API int
+ClientSVControlBlock_getNoASDU(ClientSVControlBlock self);
+
+
+/** @} */
+
+/**
+ * @defgroup IEC61850_CLIENT_GOOSE Client side GOOSE control block handling functions
+ *
+ * @{
+ */
+
+/*********************************************************
+ * GOOSE services handling (MMS part)
+ ********************************************************/
+
+/** Enable GOOSE publisher GoCB block element */
+#define GOCB_ELEMENT_GO_ENA       1
+
+/** GOOSE ID GoCB block element */
+#define GOCB_ELEMENT_GO_ID        2
+
+/** Data set GoCB block element */
+#define GOCB_ELEMENT_DATSET       4
+
+/** Configuration revision GoCB block element (this is usually read-only) */
+#define GOCB_ELEMENT_CONF_REV     8
+
+/** Need commission GoCB block element (read-only according to 61850-7-2) */
+#define GOCB_ELEMENT_NDS_COMM    16
+
+/** Destination address GoCB block element (read-only according to 61850-7-2) */
+#define GOCB_ELEMENT_DST_ADDRESS 32
+
+/** Minimum time GoCB block element (read-only according to 61850-7-2) */
+#define GOCB_ELEMENT_MIN_TIME    64
+
+/** Maximum time GoCB block element (read-only according to 61850-7-2) */
+#define GOCB_ELEMENT_MAX_TIME   128
+
+/** Fixed offsets GoCB block element (read-only according to 61850-7-2) */
+#define GOCB_ELEMENT_FIXED_OFFS 256
+
+/** select all elements of the GoCB */
+#define GOCB_ELEMENT_ALL        511
+
+
+/**************************************************
+ * ClientGooseControlBlock class
+ **************************************************/
+
+LIB61850_API ClientGooseControlBlock
+ClientGooseControlBlock_create(const char* dataAttributeReference);
+
+LIB61850_API void
+ClientGooseControlBlock_destroy(ClientGooseControlBlock self);
+
+LIB61850_API bool
+ClientGooseControlBlock_getGoEna(ClientGooseControlBlock self);
+
+LIB61850_API void
+ClientGooseControlBlock_setGoEna(ClientGooseControlBlock self, bool goEna);
+
+LIB61850_API const char*
+ClientGooseControlBlock_getGoID(ClientGooseControlBlock self);
+
+LIB61850_API void
+ClientGooseControlBlock_setGoID(ClientGooseControlBlock self, const char* goID);
+
+LIB61850_API const char*
+ClientGooseControlBlock_getDatSet(ClientGooseControlBlock self);
+
+LIB61850_API void
+ClientGooseControlBlock_setDatSet(ClientGooseControlBlock self, const char* datSet);
+
+LIB61850_API uint32_t
+ClientGooseControlBlock_getConfRev(ClientGooseControlBlock self);
+
+LIB61850_API bool
+ClientGooseControlBlock_getNdsComm(ClientGooseControlBlock self);
+
+LIB61850_API uint32_t
+ClientGooseControlBlock_getMinTime(ClientGooseControlBlock self);
+
+LIB61850_API uint32_t
+ClientGooseControlBlock_getMaxTime(ClientGooseControlBlock self);
+
+LIB61850_API bool
+ClientGooseControlBlock_getFixedOffs(ClientGooseControlBlock self);
+
+LIB61850_API PhyComAddress
+ClientGooseControlBlock_getDstAddress(ClientGooseControlBlock self);
+
+LIB61850_API void
+ClientGooseControlBlock_setDstAddress(ClientGooseControlBlock self, PhyComAddress value);
+
+LIB61850_API DEPRECATED MmsValue* /* MMS_OCTET_STRING */
+ClientGooseControlBlock_getDstAddress_addr(ClientGooseControlBlock self);
+
+LIB61850_API DEPRECATED void
+ClientGooseControlBlock_setDstAddress_addr(ClientGooseControlBlock self, MmsValue* macAddr);
+
+LIB61850_API DEPRECATED uint8_t
+ClientGooseControlBlock_getDstAddress_priority(ClientGooseControlBlock self);
+
+LIB61850_API DEPRECATED void
+ClientGooseControlBlock_setDstAddress_priority(ClientGooseControlBlock self, uint8_t priorityValue);
+
+LIB61850_API DEPRECATED uint16_t
+ClientGooseControlBlock_getDstAddress_vid(ClientGooseControlBlock self);
+
+LIB61850_API DEPRECATED void
+ClientGooseControlBlock_setDstAddress_vid(ClientGooseControlBlock self, uint16_t vidValue);
+
+LIB61850_API DEPRECATED uint16_t
+ClientGooseControlBlock_getDstAddress_appid(ClientGooseControlBlock self);
+
+LIB61850_API DEPRECATED void
+ClientGooseControlBlock_setDstAddress_appid(ClientGooseControlBlock self, uint16_t appidValue);
+
+
+/*********************************************************
+ * GOOSE services (access to GOOSE Control Blocks (GoCB))
+ ********************************************************/
+
+/**
+ * \brief Read access to attributes of a GOOSE control block (GoCB) at the connected server.
+ *
+ * A GoCB contains the configuration values for a single GOOSE publisher.
+ *
+ * The requested GoCB has to be specified by its object IEC 61850 ACSI object reference. E.g.
+ *
+ * "simpleIOGernericIO/LLN0.gcbEvents"
+ *
+ * This function is used to perform the actual read service for the GoCB values.
+ * To access the received values the functions of ClientGooseControlBlock have to be used.
+ *
+ * If called with a NULL argument for the updateGoCB parameter a new ClientGooseControlBlock instance is created
+ * and populated with the values received by the server. It is up to the user to release this object by
+ * calling the ClientGooseControlBlock_destroy function when the object is no longer needed. If called with a reference
+ * to an existing ClientGooseControlBlock instance the values of the attributes will be updated and no new instance
+ * will be created.
+ *
+ * Note: This function maps to a single MMS read request to retrieve the complete GoCB at once.
+ *
+ * \param connection the connection object
+ * \param error the error code if an error occurs
+ * \param goCBReference IEC 61850-7-2 ACSI object reference of the GOOSE control block
+ * \param updateRcb a reference to an existing ClientGooseControlBlock instance or NULL
+ *
+ * \return new ClientGooseControlBlock instance or the instance provided by the user with
+ *         the updateRcb parameter.
+ */
+LIB61850_API ClientGooseControlBlock
+IedConnection_getGoCBValues(IedConnection self, IedClientError* error, const char* goCBReference, ClientGooseControlBlock updateGoCB);
+
+typedef void
+(*IedConnection_GetGoCBValuesHandler) (uint32_t invokeId, void* parameter, IedClientError err, ClientGooseControlBlock goCB);
+
+/**
+ * \brief Read access to attributes of a GOOSE control block (GoCB) at the connected server (async version)
+ *
+ * A GoCB contains the configuration values for a single GOOSE publisher.
+ *
+ * The requested GoCB has to be specified by its object IEC 61850 ACSI object reference. E.g.
+ *
+ * "simpleIOGernericIO/LLN0.gcbEvents"
+ *
+ * This function is used to perform the actual read service for the GoCB values.
+ * To access the received values the functions of ClientGooseControlBlock have to be used.
+ *
+ * If called with a NULL argument for the updateGoCB parameter a new ClientGooseControlBlock instance is created
+ * and populated with the values received by the server. It is up to the user to release this object by
+ * calling the ClientGooseControlBlock_destroy function when the object is no longer needed. If called with a reference
+ * to an existing ClientGooseControlBlock instance the values of the attributes will be updated and no new instance
+ * will be created.
+ *
+ * Note: This function maps to a single MMS read request to retrieve the complete GoCB at once.
+ *
+ * \param connection the connection object
+ * \param error the error code if an error occurs
+ * \param goCBReference IEC 61850-7-2 ACSI object reference of the GOOSE control block
+ * \param updateRcb a reference to an existing ClientGooseControlBlock instance or NULL
+ * \param handler the user callback that is called when the service is completed or timed out
+ * \param parameter user provided parameter that is passed to the callback handler
+ *
+ * \return the invoke ID of the request
+ */
+LIB61850_API uint32_t
+IedConnection_getGoCBValuesAsync(IedConnection self, IedClientError* error, const char* goCBReference, ClientGooseControlBlock updateGoCB,
+    IedConnection_GetGoCBValuesHandler handler, void* parameter);
+
+/**
+ * \brief Write access to attributes of a GOOSE control block (GoCB) at the connected server
+ *
+ * The GoCB and the values to be written are specified with the goCB parameter.
+ *
+ * The parametersMask parameter specifies which attributes of the remote GoCB have to be set by this request.
+ * You can specify multiple attributes by ORing the defined bit values. If all attributes have to be written
+ * GOCB_ELEMENT_ALL can be used.
+ *
+ * The singleRequest parameter specifies the mapping to the corresponding MMS write request. Standard compliant
+ * servers should accept both variants. But some server accept only one variant. Then the value of this parameter
+ * will be of relevance.
+ *
+ * \param connection the connection object
+ * \param error the error code if an error occurs
+ * \param goCB ClientGooseControlBlock instance that actually holds the parameter
+ *            values to be written.
+ * \param parametersMask specifies the parameters contained in the setGoCBValues request.
+ * \param singleRequest specifies if the seGoCBValues services is mapped to a single MMS write request containing
+ *        multiple variables or to multiple MMS write requests.
+ */
+LIB61850_API void
+IedConnection_setGoCBValues(IedConnection self, IedClientError* error, ClientGooseControlBlock goCB,
+        uint32_t parametersMask, bool singleRequest);
+
+/**
+ * \brief Write access to attributes of a GOOSE control block (GoCB) at the connected server (async version)
+ *
+ * The GoCB and the values to be written are specified with the goCB parameter.
+ *
+ * The parametersMask parameter specifies which attributes of the remote GoCB have to be set by this request.
+ * You can specify multiple attributes by ORing the defined bit values. If all attributes have to be written
+ * GOCB_ELEMENT_ALL can be used.
+ *
+ * The singleRequest parameter specifies the mapping to the corresponding MMS write request. Standard compliant
+ * servers should accept both variants. But some server accept only one variant. Then the value of this parameter
+ * will be of relevance.
+ *
+ * \param connection the connection object
+ * \param error the error code if an error occurs
+ * \param goCB ClientGooseControlBlock instance that actually holds the parameter
+ *            values to be written.
+ * \param parametersMask specifies the parameters contained in the setGoCBValues request.
+ * \param singleRequest specifies if the seGoCBValues services is mapped to a single MMS write request containing
+ *        multiple variables or to multiple MMS write requests.
+ * \param handler the user callback that is called when the service is completed or timed out
+ * \param parameter user provided parameter that is passed to the callback handler
+ *
+ * \return the invoke ID of the request
+ */
+LIB61850_API uint32_t
+IedConnection_setGoCBValuesAsync(IedConnection self, IedClientError* error, ClientGooseControlBlock goCB,
+    uint32_t parametersMask, bool singleRequest, IedConnection_GenericServiceHandler handler, void* parameter);
+
+/** @} */
+
+
+/****************************************
+ * Data model access services
+ ****************************************/
+
+/**
+ * @defgroup IEC61850_CLIENT_DATA_ACCESS Client side data access (read/write) service functions
+ *
+ * @{
+ */
+
+/**
+ * \brief read a functional constrained data attribute (FCDA) or functional constrained data (FCD).
+ *
+ * \param self  the connection object to operate on
+ * \param error the error code if an error occurs
+ * \param object reference of the object/attribute to read
+ * \param fc the functional constraint of the data attribute or data object to read
+ *
+ * \return the MmsValue instance of the received value or NULL if the request failed
+ */
+LIB61850_API MmsValue*
+IedConnection_readObject(IedConnection self, IedClientError* error, const char* dataAttributeReference, FunctionalConstraint fc);
+
+typedef void
+(*IedConnection_ReadObjectHandler) (uint32_t invokeId, void* parameter, IedClientError err, MmsValue* value);
+
+/**
+ * \brief read a functional constrained data attribute (FCDA) or functional constrained data (FCD) - async version
+ *
+ * \param self  the connection object to operate on
+ * \param error the error code if an error occurs
+ * \param object reference of the object/attribute to read
+ * \param fc the functional constraint of the data attribute or data object to read
+ * \param handler the user provided callback handler
+ * \param parameter user provided parameter that is passed to the callback handler
+ *
+ * \return the invoke ID of the request
+ */
+LIB61850_API uint32_t
+IedConnection_readObjectAsync(IedConnection self, IedClientError* error, const char* objRef, FunctionalConstraint fc,
+        IedConnection_ReadObjectHandler handler, void* parameter);
+
+/**
+ * \brief write a functional constrained data attribute (FCDA) or functional constrained data (FCD).
+ *
+ * \param self  the connection object to operate on
+ * \param error the error code if an error occurs
+ * \param object reference of the object/attribute to write
+ * \param fc the functional constraint of the data attribute or data object to write
+ * \param value the MmsValue to write (has to be of the correct type - MMS_STRUCTURE for FCD)
+ */
+LIB61850_API void
+IedConnection_writeObject(IedConnection self, IedClientError* error, const char* dataAttributeReference, FunctionalConstraint fc,
+        MmsValue* value);
+
+/**
+ * \brief write a functional constrained data attribute (FCDA) or functional constrained data (FCD) - async version
+ *
+ * \param self  the connection object to operate on
+ * \param error the error code if an error occurs
+ * \param object reference of the object/attribute to write
+ * \param fc the functional constraint of the data attribute or data object to write
+ * \param value the MmsValue to write (has to be of the correct type - MMS_STRUCTURE for FCD)
+ * \param handler the user provided callback handler
+ * \param parameter user provided parameter that is passed to the callback handler
+ *
+ * \return the invoke ID of the request
+ */
+LIB61850_API uint32_t
+IedConnection_writeObjectAsync(IedConnection self, IedClientError* error, const char* objectReference,
+        FunctionalConstraint fc, MmsValue* value, IedConnection_GenericServiceHandler handler, void* parameter);
+
+/**
+ * \brief read a functional constrained data attribute (FCDA) of type boolean
+ *
+ * \param self  the connection object to operate on
+ * \param error the error code if an error occurs
+ * \param object reference of the data attribute to read
+ * \param fc the functional constraint of the data attribute to read
+ */
+LIB61850_API bool
+IedConnection_readBooleanValue(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc);
+
+/**
+ * \brief read a functional constrained data attribute (FCDA) of type float
+ *
+ * \param self  the connection object to operate on
+ * \param error the error code if an error occurs
+ * \param object reference of the data attribute to read
+ * \param fc the functional constraint of the data attribute to read
+ */
+LIB61850_API float
+IedConnection_readFloatValue(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc);
+
+/**
+ * \brief read a functional constrained data attribute (FCDA) of type VisibleString or MmsString
+ *
+ * NOTE: the returned char buffer is dynamically allocated and has to be freed by the caller!
+ *
+ * \param self  the connection object to operate on
+ * \param error the error code if an error occurs
+ * \param object reference of the data attribute to read
+ * \param fc the functional constraint of the data attribute to read
+ *
+ * \return a C string representation of the value. Has to be freed by the caller!
+ */
+LIB61850_API char*
+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
+ *
+ * \param self  the connection object to operate on
+ * \param error the error code if an error occurs
+ * \param object reference of the data attribute to read
+ * \param fc the functional constraint of the data attribute to read
+ *
+ * \return an int32_t value of the read data attributes
+ */
+LIB61850_API int32_t
+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 int64_t
+ *
+ * \param self  the connection object to operate on
+ * \param error the error code if an error occurs
+ * \param object reference of the data attribute to read
+ * \param fc the functional constraint of the data attribute to read
+ *
+ * \return an int64_t value of the read data attributes
+ */
+LIB61850_API int64_t
+IedConnection_readInt64Value(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
+ *
+ * \param self  the connection object to operate on
+ * \param error the error code if an error occurs
+ * \param object reference of the data attribute to read
+ * \param fc the functional constraint of the data attribute to read
+ *
+ * \return an uint32_t value of the read data attributes
+ */
+LIB61850_API uint32_t
+IedConnection_readUnsigned32Value(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc);
+
+/**
+ * \brief read a functional constrained data attribute (FCDA) of type Timestamp (UTC Time)
+ *
+ *  NOTE: If the timestamp parameter is set to NULL the function allocates a new timestamp instance. Otherwise the
+ *  return value is a pointer to the user provided timestamp instance. The new timestamp instance has to be freed by
+ *  the caller of the function.
+ *
+ * \param self  the connection object to operate on
+ * \param error the error code if an error occurs
+ * \param object reference of the data attribute to read
+ * \param fc the functional constraint of the data attribute to read
+ * \param timestamp a pointer to a user provided timestamp instance or NULL
+ *
+ * \return the timestamp value
+ */
+LIB61850_API Timestamp*
+IedConnection_readTimestampValue(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc,
+        Timestamp* timeStamp);
+
+/**
+ * \brief read a functional constrained data attribute (FCDA) of type Quality
+ *
+ * \param self  the connection object to operate on
+ * \param error the error code if an error occurs
+ * \param object reference of the data attribute to read
+ * \param fc the functional constraint of the data attribute to read
+ *
+ * \return the timestamp value
+ */
+LIB61850_API Quality
+IedConnection_readQualityValue(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc);
+
+/**
+ * \brief write a functional constrained data attribute (FCDA) of type boolean
+ *
+ * \param self  the connection object to operate on
+ * \param error the error code if an error occurs
+ * \param object reference of the data attribute to read
+ * \param fc the functional constraint of the data attribute or data object to write
+ * \param value the boolean value to write
+ */
+LIB61850_API void
+IedConnection_writeBooleanValue(IedConnection self, IedClientError* error, const char* objectReference,
+        FunctionalConstraint fc, bool value);
+
+/**
+ * \brief write a functional constrained data attribute (FCDA) of type integer
+ *
+ * \param self  the connection object to operate on
+ * \param error the error code if an error occurs
+ * \param object reference of the data attribute to read
+ * \param fc the functional constraint of the data attribute or data object to write
+ * \param value the int32_t value to write
+ */
+LIB61850_API void
+IedConnection_writeInt32Value(IedConnection self, IedClientError* error, const char* objectReference,
+        FunctionalConstraint fc, int32_t value);
+
+/**
+ * \brief write a functional constrained data attribute (FCDA) of type unsigned (integer)
+ *
+ * \param self  the connection object to operate on
+ * \param error the error code if an error occurs
+ * \param object reference of the data attribute to read
+ * \param fc the functional constraint of the data attribute or data object to write
+ * \param value the uint32_t value to write
+ */
+LIB61850_API void
+IedConnection_writeUnsigned32Value(IedConnection self, IedClientError* error, const char* objectReference,
+        FunctionalConstraint fc, uint32_t value);
+
+/**
+ * \brief write a functional constrained data attribute (FCDA) of type float
+ *
+ * \param self  the connection object to operate on
+ * \param error the error code if an error occurs
+ * \param object reference of the data attribute to read
+ * \param fc the functional constraint of the data attribute or data object to write
+ * \param value the float value to write
+ */
+LIB61850_API void
+IedConnection_writeFloatValue(IedConnection self, IedClientError* error, const char* objectReference,
+        FunctionalConstraint fc, float value);
+
+LIB61850_API void
+IedConnection_writeVisibleStringValue(IedConnection self, IedClientError* error, const char* objectReference,
+        FunctionalConstraint fc, char* value);
+
+LIB61850_API void
+IedConnection_writeOctetString(IedConnection self, IedClientError* error, const char* objectReference,
+        FunctionalConstraint fc, uint8_t* value, int valueLength);
+
+/** @} */
+
+/********************************************
+ * Reporting services
+ ********************************************/
+
+/**
+ * @defgroup IEC61850_CLIENT_REPORTS Client side report handling services, functions, and data types
+ *
+ * @{
+ */
+
+/**
+ * \brief Read access to attributes of a report control block (RCB) at the connected server
+ *
+ * The requested RCB has to be specified by its object reference. E.g.
+ *
+ * "simpleIOGenericIO/LLN0.RP.EventsRCB01"
+ *
+ * or
+ *
+ * "simpleIOGenericIO/LLN0.BR.EventsBRCB01"
+ *
+ * Report control blocks have either "RP" or "BR" as part of their name following the logical node part.
+ * "RP" is part of the name of unbuffered RCBs whilst "BR" is part of the name of buffered RCBs.
+ *
+ * This function is used to perform the actual read service. To access the received values the functions
+ * of ClientReportControlBlock have to be used.
+ *
+ * If called with a NULL argument for the updateRcb parameter a new ClientReportControlBlock instance is created
+ * and populated with the values received by the server. It is up to the user to release this object by
+ * calling the ClientReportControlBlock_destroy function when the object is no longer needed. If called with a reference
+ * to an existing ClientReportControlBlock instance the values of the attributes will be updated and no new instance
+ * will be created.
+ *
+ * Note: This function maps to a single MMS read request to retrieve the complete RCB at once.
+ *
+ * \param connection the connection object
+ * \param error the error code if an error occurs
+ * \param rcbReference object reference of the report control block
+ * \param updateRcb a reference to an existing ClientReportControlBlock instance or NULL
+ *
+ * \return new ClientReportControlBlock instance or the instance provided by the user with
+ *         the updateRcb parameter.
+ */
+LIB61850_API ClientReportControlBlock
+IedConnection_getRCBValues(IedConnection self, IedClientError* error, const char* rcbReference,
+        ClientReportControlBlock updateRcb);
+
+typedef void
+(*IedConnection_GetRCBValuesHandler) (uint32_t invokeId, void* parameter, IedClientError err, ClientReportControlBlock rcb);
+
+LIB61850_API uint32_t
+IedConnection_getRCBValuesAsync(IedConnection self, IedClientError* error, const char* rcbReference, ClientReportControlBlock updateRcb,
+        IedConnection_GetRCBValuesHandler handler, void* parameter);
+
+/** Describes the reason for the inclusion of the element in the report */
+typedef int ReasonForInclusion;
+
+/** the element is not included in the received report */
+#define IEC61850_REASON_NOT_INCLUDED 0
+
+/** the element is included due to a change of the data value */
+#define IEC61850_REASON_DATA_CHANGE 1
+
+/** the element is included due to a change in the quality of data */
+#define IEC61850_REASON_QUALITY_CHANGE 2
+
+/** the element is included due to an update of the data value */
+#define IEC61850_REASON_DATA_UPDATE 4
+
+/** the element is included due to a periodic integrity report task */
+#define IEC61850_REASON_INTEGRITY 8
+
+/** the element is included due to a general interrogation by the client */
+#define IEC61850_REASON_GI 16
+
+/** the reason for inclusion is unknown (e.g. report is not configured to include reason-for-inclusion) */
+#define IEC61850_REASON_UNKNOWN 32
+
+/* Element encoding mask values for ClientReportControlBlock */
+
+/** include the report ID into the setRCB request */
+#define RCB_ELEMENT_RPT_ID            1
+
+/** include the report enable element into the setRCB request */
+#define RCB_ELEMENT_RPT_ENA           2
+
+/** include the reservation element into the setRCB request (only available in unbuffered RCBs!) */
+#define RCB_ELEMENT_RESV              4
+
+/** include the data set element into the setRCB request */
+#define RCB_ELEMENT_DATSET            8
+
+/** include the configuration revision element into the setRCB request */
+#define RCB_ELEMENT_CONF_REV         16
+
+/** include the option fields element into the setRCB request */
+#define RCB_ELEMENT_OPT_FLDS         32
+
+/** include the bufTm (event buffering time) element into the setRCB request */
+#define RCB_ELEMENT_BUF_TM           64
+
+/** include the sequence number element into the setRCB request (should be used!) */
+#define RCB_ELEMENT_SQ_NUM          128
+
+/** include the trigger options element into the setRCB request */
+#define RCB_ELEMENT_TRG_OPS         256
+
+/** include the integrity period element into the setRCB request */
+#define RCB_ELEMENT_INTG_PD         512
+
+/** include the GI (general interrogation) element into the setRCB request */
+#define RCB_ELEMENT_GI             1024
+
+/** include the purge buffer element into the setRCB request (only available in buffered RCBs) */
+#define RCB_ELEMENT_PURGE_BUF      2048
+
+/** include the entry ID element into the setRCB request (only available in buffered RCBs) */
+#define RCB_ELEMENT_ENTRY_ID       4096
+
+/** include the time of entry element into the setRCB request (only available in buffered RCBs) */
+#define RCB_ELEMENT_TIME_OF_ENTRY  8192
+
+/** include the reservation time element into the setRCB request (only available in buffered RCBs) */
+#define RCB_ELEMENT_RESV_TMS      16384
+
+/** include the owner element into the setRCB request */
+#define RCB_ELEMENT_OWNER         32768
+
+/**
+ * \brief Write access to attributes of a report control block (RCB) at the connected server
+ *
+ * The requested RCB has to be specified by its object reference (see also IedConnection_getRCBValues).
+ * The object reference for the referenced RCB is contained in the provided ClientReportControlBlock instance.
+ *
+ * The parametersMask parameter specifies which attributes of the remote RCB have to be set by this request.
+ * You can specify multiple attributes by ORing the defined bit values.
+ *
+ * The singleRequest parameter specifies the mapping to the corresponding MMS write request. Standard compliant
+ * servers should accept both variants. But some server accept only one variant. Then the value of this parameter
+ * will be of relevance.
+ *
+ * \param connection the connection object
+ * \param error the error code if an error occurs
+ * \param rcb object reference of the ClientReportControlBlock instance that actually holds the parameter
+ *            values to be written.
+ * \param parametersMask specifies the parameters contained in the setRCBValues request.
+ * \param singleRequest specifies if the setRCBValues services is mapped to a single MMS write request containing
+ *        multiple variables or to multiple MMS write requests.
+ */
+LIB61850_API void
+IedConnection_setRCBValues(IedConnection self, IedClientError* error, ClientReportControlBlock rcb,
+        uint32_t parametersMask, bool singleRequest);
+
+LIB61850_API uint32_t
+IedConnection_setRCBValuesAsync(IedConnection self, IedClientError* error, ClientReportControlBlock rcb,
+        uint32_t parametersMask, bool singleRequest, IedConnection_GenericServiceHandler handler, void* parameter);
+
+/**
+ * \brief Callback function for receiving reports
+ *
+ * \param parameter a user provided parameter that is handed to the callback function
+ * \param report a ClientReport instance that holds the informations contained in the received report
+ */
+typedef void (*ReportCallbackFunction) (void* parameter, ClientReport report);
+
+/**
+ * \brief Install a report handler function for the specified report control block (RCB)
+ *
+ * This function will replace a report handler set earlier for the specified RCB. The report handler
+ * will be called whenever a report for the specified RCB is received.
+ * Please note that this function should be called whenever the RCB data set is changed or updated.
+ * Otherwise the internal data structures storing the received data set values will not be updated
+ * correctly.
+ *
+ * \note Replacing a report handler you only have to call this function. There is no separate call to
+ * IedConnection_uninstallReportHandler() required.
+ *
+ * \note Do not call this function inside of the ReportCallbackFunction. Doing so will cause a deadlock.
+ *
+ * \param self the connection object
+ * \param rcbReference object reference of the report control block
+ * \param rptId a string that identifies the report. If the rptId is not available then the
+ *        rcbReference is used to identify the report.
+ * \param handler user provided callback function to be invoked when a report is received.
+ * \param handlerParameter user provided parameter that will be passed to the callback function
+ */
+LIB61850_API void
+IedConnection_installReportHandler(IedConnection self, const char* rcbReference, const char* rptId, ReportCallbackFunction handler,
+        void* handlerParameter);
+
+/**
+ * \brief uninstall a report handler function for the specified report control block (RCB)
+ *
+ * \note Do not call this function inside of the ReportCallbackFunction. Doing so will cause a deadlock.
+ *
+ * \param self the connection object
+ * \param rcbReference object reference of the report control block
+ */
+LIB61850_API void
+IedConnection_uninstallReportHandler(IedConnection self, const char* rcbReference);
+
+/**
+ * \brief trigger a general interrogation (GI) report for the specified report control block (RCB)
+ *
+ * The RCB must have been enabled and GI set as trigger option before this command can be performed.
+ *
+ * \deprecated Use ClientReportControlBlock_setGI instead
+ *
+ * \param connection the connection object
+ * \param error the error code if an error occurs
+ * \param rcbReference object reference of the report control block
+ */
+LIB61850_API void
+IedConnection_triggerGIReport(IedConnection self, IedClientError* error, const char* rcbReference);
+
+/****************************************
+ * Access to received reports
+ ****************************************/
+
+/**
+ * \brief get the name of the report data set
+ *
+ * NOTE: the returned string is only valid as long as the ClientReport instance exists!
+ *
+ * \param self the ClientReport instance
+ * \return report data set name as 0 terminated string
+ */
+LIB61850_API const char*
+ClientReport_getDataSetName(ClientReport self);
+
+/**
+ * \brief return the received data set values of the report
+ *
+ * NOTE: The returned MmsValue instance is handled by the library and only valid as long as the
+ * ClientReport instance exists! It should not be used outside the report callback handler to
+ * avoid concurrency issues.
+ *
+ * \param self the ClientReport instance
+ * \return an MmsValue array instance containing the data set values
+ */
+LIB61850_API MmsValue*
+ClientReport_getDataSetValues(ClientReport self);
+
+/**
+ * \brief return reference (name) of the server RCB associated with this ClientReport object
+ *
+ * \param self the ClientReport instance
+ * \return report control block reference as string
+ */
+LIB61850_API char*
+ClientReport_getRcbReference(ClientReport self);
+
+/**
+ * \brief return RptId of the server RCB associated with this ClientReport object
+ *
+ * \param self the ClientReport instance
+ * \return report control block reference as string
+ */
+LIB61850_API char*
+ClientReport_getRptId(ClientReport self);
+
+/**
+ * \brief get the reason code (reason for inclusion) for a specific report data set element
+ *
+ * \param self the ClientReport instance
+ * \param elementIndex index of the data set element (starting with 0)
+ *
+ * \return reason code for the inclusion of the specified element
+ */
+LIB61850_API ReasonForInclusion
+ClientReport_getReasonForInclusion(ClientReport self, int elementIndex);
+
+/**
+ * \brief get the entry ID of the report
+ *
+ * Returns the entryId of the report if included in the report. Otherwise returns NULL.
+ *
+ * \param self the ClientReport instance
+ *
+ * \return entryId or NULL
+ */
+LIB61850_API MmsValue*
+ClientReport_getEntryId(ClientReport self);
+
+/**
+ * \brief determine if the last received report contains a timestamp
+ *
+ * \param self the ClientReport instance
+ *
+ * \return true if the report contains a timestamp, false otherwise
+ */
+LIB61850_API bool
+ClientReport_hasTimestamp(ClientReport self);
+
+/**
+ * \brief determine if the last received report contains a sequence number
+ *
+ * \param self the ClientReport instance
+ *
+ * \return true if the report contains a sequence number, false otherwise
+ */
+LIB61850_API bool
+ClientReport_hasSeqNum(ClientReport self);
+
+/**
+ * \brief get the value of the sequence number
+ *
+ * NOTE: The returned value is undefined if the sequence number is not present in report
+ *
+ * \param self the ClientReport instance
+ *
+ * \returns the number of the sequence number when present
+ */
+LIB61850_API uint16_t
+ClientReport_getSeqNum(ClientReport self);
+
+/**
+ * \brief determine if the last received report contains the data set name
+ *
+ * \param self the ClientReport instance
+ *
+ * \return true if the report contains the data set name, false otherwise
+ */
+LIB61850_API bool
+ClientReport_hasDataSetName(ClientReport self);
+
+/**
+ * \brief determine if the last received report contains reason-for-inclusion information
+ *
+ * \param self the ClientReport instance
+ *
+ * \return true if the report contains reason-for-inclusion information, false otherwise
+ */
+LIB61850_API bool
+ClientReport_hasReasonForInclusion(ClientReport self);
+
+/**
+ * \brief determine if the last received report contains the configuration revision
+ *
+ * \param self the ClientReport instance
+ *
+ * \return true if the report contains the configuration revision, false otherwise
+ */
+LIB61850_API bool
+ClientReport_hasConfRev(ClientReport self);
+
+/**
+ * \brief get the value of the configuration revision
+ *
+ * NOTE: The returned value is undefined if configuration revision is not present in report
+ *
+ * \param self the ClientReport instance
+ *
+ * \returns the number of the configuration revision
+ */
+LIB61850_API uint32_t
+ClientReport_getConfRev(ClientReport self);
+
+/**
+ * \brief indicates if the report contains the bufOvfl (buffer overflow) flag
+ *
+ * \param self the ClientReport instance
+ *
+ * \returns true if the report contains the bufOvfl flag, false otherwise
+ */
+LIB61850_API bool
+ClientReport_hasBufOvfl(ClientReport self);
+
+/**
+ * \brief get the value of the bufOvfl flag
+ *
+ * \param self the ClientReport instance
+ *
+ * \returns true if bufOvfl is set, false otherwise
+ */
+LIB61850_API bool
+ClientReport_getBufOvfl(ClientReport self);
+
+/**
+ * \brief indicates if the report contains data references for the reported data set members
+ *
+ * \param self the ClientReport instance
+ *
+ * \returns true if the report contains data-references, false otherwise
+ */
+LIB61850_API bool
+ClientReport_hasDataReference(ClientReport self);
+
+/**
+ * \brief get the data-reference of the element of the report data set
+ *
+ * This function will only return a non-NULL value if the received report contains data-references.
+ * This can be determined by the ClientReport_hasDataReference function.
+ * NOTE: The returned string is allocated and hold by the ClientReport instance and is only valid until
+ * the ClientReport instance exists!
+ *
+ * \param self the ClientReport instance
+ * \param elementIndex  index of the data set element (starting with 0)
+ *
+ * \param the data reference as string as provided by the report or NULL if the data reference is not available
+ */
+LIB61850_API const char*
+ClientReport_getDataReference(ClientReport self, int elementIndex);
+
+
+/**
+ * \brief get the timestamp of the report
+ *
+ * Returns the timestamp of the report if included in the report. Otherwise the value is undefined.
+ * Use the ClientReport_hasTimestamp function first to figure out if the timestamp is valid
+ *
+ * \param self the ClientReport instance
+ *
+ * \return the timestamp as milliseconds since 1.1.1970 UTC
+ */
+LIB61850_API uint64_t
+ClientReport_getTimestamp(ClientReport self);
+
+/**
+ * \brief indicates if the report contains a sub sequence number and a more segments follow flags (for segmented reporting)
+ *
+ * \param self the ClientReport instance
+ *
+ * \returns true if the report contains sub-sequence-number and more-follows-flag, false otherwise
+ */
+LIB61850_API bool
+ClientReport_hasSubSeqNum(ClientReport self);
+
+/**
+ * \brief get the sub sequence number of the report (for segmented reporting)
+ *
+ * Returns the sub sequence number of the report. This is 0 for the first report of a segmented report and
+ * will be increased by one for each report segment.
+ *
+ * \param self the ClientReport instance
+ *
+ * \return the sub sequence number of the last received report message.
+ */
+LIB61850_API uint16_t
+ClientReport_getSubSeqNum(ClientReport self);
+
+/**
+ * \brief get the more segments follow flag of the received report segment (for segmented reporting)
+ *
+ * Will return true in case this is part of a segmented report and more report segments will follow or false, if
+ * the current report is not a segmented report or is the last segment of a segmented report.
+ *
+ * \param self the ClientReport instance
+ *
+ * \return true when more segments of the current report will follow, false otherwise
+ */
+LIB61850_API bool
+ClientReport_getMoreSeqmentsFollow(ClientReport self);
+
+/**
+ * \brief get the reason for inclusion of as a human readable string
+ *
+ * \param reasonCode
+ *
+ * \return the reason for inclusion as static human readable string
+ */
+LIB61850_API char*
+ReasonForInclusion_getValueAsString(ReasonForInclusion reasonCode);
+
+/**************************************************
+ * ClientReportControlBlock access class
+ **************************************************/
+
+LIB61850_API ClientReportControlBlock
+ClientReportControlBlock_create(const char* rcbReference);
+
+LIB61850_API void
+ClientReportControlBlock_destroy(ClientReportControlBlock self);
+
+LIB61850_API char*
+ClientReportControlBlock_getObjectReference(ClientReportControlBlock self);
+
+LIB61850_API bool
+ClientReportControlBlock_isBuffered(ClientReportControlBlock self);
+
+LIB61850_API const char*
+ClientReportControlBlock_getRptId(ClientReportControlBlock self);
+
+LIB61850_API void
+ClientReportControlBlock_setRptId(ClientReportControlBlock self, const char* rptId);
+
+LIB61850_API bool
+ClientReportControlBlock_getRptEna(ClientReportControlBlock self);
+
+LIB61850_API void
+ClientReportControlBlock_setRptEna(ClientReportControlBlock self, bool rptEna);
+
+LIB61850_API bool
+ClientReportControlBlock_getResv(ClientReportControlBlock self);
+
+LIB61850_API void
+ClientReportControlBlock_setResv(ClientReportControlBlock self, bool resv);
+
+LIB61850_API const char*
+ClientReportControlBlock_getDataSetReference(ClientReportControlBlock self);
+
+/**
+ * \brief set the data set to be observed by the RCB
+ *
+ * The data set reference is a mixture of MMS and IEC 61850 syntax! In general the reference has
+ * the form:
+ * LDName/LNName$DataSetName
+ *
+ * e.g. "simpleIOGenericIO/LLN0$Events"
+ *
+ * It is standard that data sets are defined in LN0 logical nodes. But this is not mandatory.
+ *
+ * Note: As a result of changing the data set the server will increase the confRev attribute of the RCB.
+ *
+ * \param self the RCB instance
+ * \param dataSetReference the reference of the data set
+ */
+LIB61850_API void
+ClientReportControlBlock_setDataSetReference(ClientReportControlBlock self, const char* dataSetReference);
+
+LIB61850_API uint32_t
+ClientReportControlBlock_getConfRev(ClientReportControlBlock self);
+
+/**
+ * \brief Gets the OptFlds parameter of the RCB (decides what information to include in a report)
+ *
+ * \param self the RCB instance
+ *
+ * \return bit field representing the optional fields of a report (uses flags from \ref REPORT_OPTIONS)
+ */
+LIB61850_API int
+ClientReportControlBlock_getOptFlds(ClientReportControlBlock self);
+
+/**
+ * \brief Set the OptFlds parameter of the RCB (decides what information to include in a report)
+ *
+ * \param self the RCB instance
+ * \param optFlds bit field representing the optional fields of a report (use flags from \ref REPORT_OPTIONS)
+ */
+LIB61850_API void
+ClientReportControlBlock_setOptFlds(ClientReportControlBlock self, int optFlds);
+
+/**
+ * \brief Get the BufTm (buffer time) parameter of the RCB
+ *
+ * The buffer time is the time to wait after a triggering event before the report is actually sent.
+ * It is used to be able to collect events that happen in a short time period and send them in a single report.
+ *
+ * \param self the RCB instance
+ */
+LIB61850_API uint32_t
+ClientReportControlBlock_getBufTm(ClientReportControlBlock self);
+
+/**
+ * \brief Set the BufTm (buffer time) parameter of the RCB
+ *
+ * The buffer time is the time to wait after a triggering event before the report is actually sent.
+ * It is used to be able to collect events that happen in a short time period and send them in a single report.
+ *
+ * \param self the RCB instance
+ * \param bufTm the buffer time in ms
+ */
+LIB61850_API void
+ClientReportControlBlock_setBufTm(ClientReportControlBlock self, uint32_t bufTm);
+
+LIB61850_API uint16_t
+ClientReportControlBlock_getSqNum(ClientReportControlBlock self);
+
+LIB61850_API int
+ClientReportControlBlock_getTrgOps(ClientReportControlBlock self);
+
+LIB61850_API void
+ClientReportControlBlock_setTrgOps(ClientReportControlBlock self, int trgOps);
+
+LIB61850_API uint32_t
+ClientReportControlBlock_getIntgPd(ClientReportControlBlock self);
+
+LIB61850_API void
+ClientReportControlBlock_setIntgPd(ClientReportControlBlock self, uint32_t intgPd);
+
+LIB61850_API bool
+ClientReportControlBlock_getGI(ClientReportControlBlock self);
+
+LIB61850_API void
+ClientReportControlBlock_setGI(ClientReportControlBlock self, bool gi);
+
+LIB61850_API bool
+ClientReportControlBlock_getPurgeBuf(ClientReportControlBlock self);
+
+/**
+ * \brief Set the "PurgeBuf" attribute value (only BRCB)
+ *
+ * When set to true the report buffer will be cleared.
+ *
+ * \param purgeBuf attribute value
+ */
+LIB61850_API void
+ClientReportControlBlock_setPurgeBuf(ClientReportControlBlock self, bool purgeBuf);
+
+/**
+ *  \brief Check if optional attribute "ResvTms" is present in BRCB
+ *
+ *  \return true when present, false otherwise
+ */
+LIB61850_API  bool
+ClientReportControlBlock_hasResvTms(ClientReportControlBlock self);
+
+LIB61850_API int16_t
+ClientReportControlBlock_getResvTms(ClientReportControlBlock self);
+
+LIB61850_API void
+ClientReportControlBlock_setResvTms(ClientReportControlBlock self, int16_t resvTms);
+
+LIB61850_API MmsValue* /* <MMS_OCTET_STRING> */
+ClientReportControlBlock_getEntryId(ClientReportControlBlock self);
+
+LIB61850_API void
+ClientReportControlBlock_setEntryId(ClientReportControlBlock self, MmsValue* entryId);
+
+LIB61850_API uint64_t
+ClientReportControlBlock_getEntryTime(ClientReportControlBlock self);
+
+LIB61850_API MmsValue* /* <MMS_OCTET_STRING> */
+ClientReportControlBlock_getOwner(ClientReportControlBlock self);
+
+
+/** @} */
+
+/****************************************
+ * Data set handling
+ ****************************************/
+
+/**
+ * @defgroup IEC61850_CLIENT_DATA_SET Client side data set service functions and data types
+ *
+ * @{
+ */
+
+/**
+ * \brief get data set values from the server
+ *
+ * The parameter dataSetReference is the name of the data set to read. It is either in the form LDName/LNodeName.dataSetName
+ * for permanent domain or VMD scope data sets or @dataSetName for an association specific data set.
+ * If the LDName part of the reference is missing the resulting data set will be of VMD scope.
+ * The received data set values are stored in a container object of type ClientDataSet that can be reused in a further
+ * read request.
+ *
+ * \param connection the connection object
+ * \param error the error code if an error occurs
+ * \param dataSetReference object reference of the data set
+ * \param dataSet a data set instance where to store the retrieved values or NULL if a new instance
+ *        shall be created.
+ *
+ * \return data set instance with retrieved values of NULL if an error occurred.
+ */
+LIB61850_API ClientDataSet
+IedConnection_readDataSetValues(IedConnection self, IedClientError* error, const char* dataSetReference, ClientDataSet dataSet);
+
+typedef void
+(*IedConnection_ReadDataSetHandler) (uint32_t invokeId, void* parameter, IedClientError err, ClientDataSet dataSet);
+
+/**
+ * \brief get data set values from the server - async version
+ *
+ * The parameter dataSetReference is the name of the data set to read. It is either in the form LDName/LNodeName.dataSetName
+ * for permanent domain or VMD scope data sets or @dataSetName for an association specific data set.
+ * If the LDName part of the reference is missing the resulting data set will be of VMD scope.
+ * The received data set values are stored in a container object of type ClientDataSet that can be reused in a further
+ * service request.
+ *
+ * \param connection the connection object
+ * \param error the error code if an error occurs
+ * \param dataSetReference object reference of the data set
+ * \param dataSet a data set instance where to store the retrieved values or NULL if a new instance
+ *        shall be created.
+ * \param handler the user provided callback handler
+ * \param parameter user provided parameter that is passed to the callback handler
+ *
+ * \return the invoke ID of the request
+ */
+LIB61850_API uint32_t
+IedConnection_readDataSetValuesAsync(IedConnection self, IedClientError* error, const char* dataSetReference, ClientDataSet dataSet,
+        IedConnection_ReadDataSetHandler handler, void* parameter);
+
+/**
+ * \brief create a new data set at the connected server device
+ *
+ * This function creates a new data set at the server. The parameter dataSetReference is the name of the new data set
+ * to create. It is either in the form LDName/LNodeName.dataSetName for permanent domain or VMD scope data sets or
+ * @dataSetName for an association specific data set. If the LDName part of the reference is missing the resulting
+ * data set will be of VMD scope.
+ *
+ * The dataSetElements parameter contains a linked list containing the object references of FCDs or FCDAs. The format of
+ * this object references is LDName/LNodeName.item(arrayIndex)component[FC].
+ *
+ * \param connection the connection object
+ * \param error the error code if an error occurs
+ * \param dataSetReference object reference of the data set
+ * \param dataSetElements a list of object references defining the members of the new data set
+ *
+ */
+LIB61850_API void
+IedConnection_createDataSet(IedConnection self, IedClientError* error, const char* dataSetReference, LinkedList /* char* */ dataSetElements);
+
+/**
+ * \brief create a new data set at the connected server device
+ *
+ * This function creates a new data set at the server. The parameter dataSetReference is the name of the new data set
+ * to create. It is either in the form LDName/LNodeName.dataSetName for permanent domain or VMD scope data sets or
+ * @dataSetName for an association specific data set. If the LDName part of the reference is missing the resulting
+ * data set will be of VMD scope.
+ *
+ * The dataSetElements parameter contains a linked list containing the object references of FCDs or FCDAs. The format of
+ * this object references is LDName/LNodeName.item(arrayIndex)component[FC].
+ *
+ * \param connection the connection object
+ * \param error the error code if an error occurs
+ * \param dataSetReference object reference of the data set
+ * \param dataSetElements a list of object references defining the members of the new data set
+ *
+ * \param handler the callback handler that is called when the response is received or timeout
+ * \param parameter user provided parameter that is passed to the callback handler
+ *
+ * \return the invoke ID of the request
+ */
+LIB61850_API uint32_t
+IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, const char* dataSetReference, LinkedList /* char* */ dataSetElements,
+        IedConnection_GenericServiceHandler handler, void* parameter);
+
+/**
+ * \brief delete a deletable data set at the connected server device
+ *
+ * This function deletes a data set at the server. The parameter dataSetReference is the name of the data set
+ * to delete. It is either in the form LDName/LNodeName.dataSetName or @dataSetName for an association specific data set.
+ *
+ *
+ * \param connection the connection object
+ * \param error the error code if an error occurs
+ * \param dataSetReference object reference of the data set
+ *
+ * \return true if data set has been deleted, false otherwise
+ */
+LIB61850_API bool
+IedConnection_deleteDataSet(IedConnection self, IedClientError* error, const char* dataSetReference);
+
+/**
+ * \brief delete a deletable data set at the connected server device - asynchronous version
+ *
+ * This function deletes a data set at the server. The parameter dataSetReference is the name of the data set
+ * to delete. It is either in the form LDName/LNodeName.dataSetName or @dataSetName for an association specific data set.
+ *
+ * The data set was deleted successfully when the callback parameter "error" is IED_ERROR_OK. Otherwise the "error"
+ * parameter contains a particular error code.
+ *
+ * \param connection the connection object
+ * \param error the error code if an error occurs
+ * \param dataSetReference object reference of the data set
+ * \param handler the callback handler that is called when the response is received or timeout
+ * \param parameter user provided parameter that is passed to the callback handler
+ *
+ * \return the invoke ID of the request
+ */
+LIB61850_API uint32_t
+IedConnection_deleteDataSetAsync(IedConnection self, IedClientError* error, const char* dataSetReference,
+        IedConnection_GenericServiceHandler handler, void* parameter);
+
+/**
+ * \brief read the data set directory
+ *
+ * The return value contains a linked list containing the object references of FCDs or FCDAs. The format of
+ * this object references is LDName/LNodeName.item(arrayIndex)component[FC].
+ *
+ * \param connection the connection object
+ * \param[out] error the error code if an error occurs
+ * \param dataSetReference object reference of the data set
+ * \param[out] isDeletable this is an output parameter indicating that the requested data set is deletable by clients.
+ *                    If this information is not required a NULL pointer can be used.
+ *
+ * \return LinkedList containing the data set elements as char* strings.
+ */
+LIB61850_API LinkedList /* <char*> */
+IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, const char* dataSetReference, bool* isDeletable);
+
+/**
+ * \brief GetDataSetDirectory response or timeout callback
+ *
+ * \param dataSetDirectory a linked list containing the object references of FCDs or FCDAs. The format of
+ *                         this object references is LDName/LNodeName.item(arrayIndex)component[FC].
+ * \param isDeletable this is an output parameter indicating that the requested data set is deletable by clients.
+ */
+typedef void
+(*IedConnection_GetDataSetDirectoryHandler) (uint32_t invokeId, void* parameter, IedClientError err, LinkedList /* <char*> */ dataSetDirectory, bool isDeletable);
+
+/**
+ * \brief read the data set directory - asynchronous version
+ *
+ * The result data is a linked list containing the object references of FCDs or FCDAs. The format of
+ * this object references is LDName/LNodeName.item(arrayIndex)component[FC].
+ *
+ * \param connection the connection object
+ * \param[out] error the error code if an error occurs
+ * \param dataSetReference object reference of the data set
+ * \param handler the callback handler that is called when the response is received or timeout
+ * \param parameter user provided parameter that is passed to the callback handler
+ *
+ * \return the invoke ID of the request
+ */
+LIB61850_API uint32_t
+IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error, const char* dataSetReference,
+        IedConnection_GetDataSetDirectoryHandler handler, void* parameter);
+
+/**
+ * \brief Write the data set values to the server
+ *
+ * The parameter dataSetReference is the name of the data set to write. It is either in the form LDName/LNodeName.dataSetName
+ * for permanent domain or VMD scope data sets or @dataSetName for an association specific data set.
+ * If the LDName part of the reference is missing the resulting data set will be of VMD scope.
+ * The values parameter has to be the same number of elements as are members in the data set. The accessResult return parameter
+ * contains a value for each data set member.
+ *
+ * \param connection the connection object
+ * \param[out] error the error code if an error occurs
+ * \param dataSetReference object reference of the data set
+ * \param values the new data set values
+ * \param[out] accessResults the access results for each data set member
+ */
+LIB61850_API void
+IedConnection_writeDataSetValues(IedConnection self, IedClientError* error, const char* dataSetReference,
+        LinkedList/*<MmsValue*>*/ values, /* OUTPUT */LinkedList* /* <MmsValue*> */accessResults);
+
+/**
+ * \brief Callback handler for asynchronous write data set values services (set data set)
+ *
+ * \param invokeId the invoke ID of the service request
+ * \param parameter used provided parameter
+ * \param err the error code if an error occurs
+ * \param accessResults the list of access results for the data set entries.
+ */
+typedef void
+(*IedConnection_WriteDataSetHandler) (uint32_t invokeId, void* parameter, IedClientError err, LinkedList /* <MmsValue*> */accessResults);
+
+/**
+ * \brief Write the data set values to the server - async version
+ *
+ * The parameter dataSetReference is the name of the data set to write. It is either in the form LDName/LNodeName.dataSetName
+ * for permanent domain or VMD scope data sets or @dataSetName for an association specific data set.
+ * If the LDName part of the reference is missing the resulting data set will be of VMD scope.
+ * The values parameter has to be the same number of elements as are members in the data set.
+ *
+ * When the service call had been successful the \ref IedConnection_WriteDataSetHandler is called with an error value of
+ * IED_ERROR_OK and a list of MmsValue instances of type data access error. These describe the access results of the individual
+ * data set entries.
+ *
+ * \param connection the connection object
+ * \param[out] error the error code if an error occurs
+ * \param dataSetReference object reference of the data set
+ * \param values the new data set values
+ * \param handler the user provided callback handler
+ * \param parameter user provided parameter that is passed to the callback handler
+ *
+ * \return the invoke ID of the request
+ */
+LIB61850_API uint32_t
+IedConnection_writeDataSetValuesAsync(IedConnection self, IedClientError* error, const char* dataSetReference,
+        LinkedList/*<MmsValue*>*/ values, IedConnection_WriteDataSetHandler handler, void* parameter);
+
+
+/********************************************************
+ * Data set object (local representation of a data set)
+ *******************************************************/
+
+/**
+ * \brief destroy an ClientDataSet instance. Has to be called by the application.
+ *
+ * Note: A ClientDataSet cannot be created directly by the application but only by the IedConnection_readDataSetValues
+ *       function. Therefore there is no public ClientDataSet_create function.
+ *
+ * \param self the ClientDataSet instance
+ */
+LIB61850_API void
+ClientDataSet_destroy(ClientDataSet self);
+
+/**
+ * \brief get the data set values locally stored in the ClientDataSet instance.
+ *
+ * This function returns a pointer to the locally stored MmsValue instance of this
+ * ClientDataSet instance. The MmsValue instance is of type MMS_ARRAY and contains one
+ * array element for each data set member.
+ * Note: This call does not invoke any interaction with the associated server. It will
+ * only provide access to already stored value. To update the values with the current values
+ * of the server the IecConnection_readDataSetValues function has to be called!
+ *
+ * \param self the ClientDataSet instance
+ *
+ * \return the locally stored data set values as MmsValue object of type MMS_ARRAY.
+ */
+LIB61850_API MmsValue*
+ClientDataSet_getValues(ClientDataSet self);
+
+/**
+ * \brief Get the object reference of the data set.
+ *
+ * \param self the ClientDataSet instance
+ *
+ * \return the object reference of the data set.
+ */
+LIB61850_API char*
+ClientDataSet_getReference(ClientDataSet self);
+
+/**
+ * \brief get the size of the data set (number of members)
+ *
+ * \param self the ClientDataSet instance
+ *
+ * \return the number of member contained in the data set.
+ */
+LIB61850_API int
+ClientDataSet_getDataSetSize(ClientDataSet self);
+
+/** @} */
+
+/************************************
+ *  Control service functions
+ ************************************/
+
+/**
+ * @defgroup IEC61850_CLIENT_CONTROL Client side control service functions
+ *
+ * @{
+ */
+
+typedef struct sControlObjectClient* ControlObjectClient;
+
+/**
+ * \brief Create a new client control object
+ *
+ * A client control object is used to handle all client side functions of a controllable
+ * data object. A controllable data object is an instance of a controllable CDC like e.g.
+ * SPC, DPC, APC, ...
+ *
+ * NOTE: This function will synchronously request information about the control object
+ * (like ctlModel) from the server. The function will block until these requests return
+ * or time-out.
+ *
+ * \param objectReference the reference of the controllable data object
+ * \param connection the connection instance where the control object has to be reached
+ *
+ * \return the newly created instance or NULL if the creation failed
+ */
+LIB61850_API ControlObjectClient
+ControlObjectClient_create(const char* objectReference, IedConnection connection);
+
+/**
+ * \brief Create a new client control object - doesn't send requests to the server (doesn't block)
+ *
+ * A client control object is used to handle all client side functions of a controllable
+ * data object. A controllable data object is an instance of a controllable CDC like e.g.
+ * SPC, DPC, APC, ...
+ *
+ * \param objectReference the reference of the controllable data object
+ * \param connection the connection instance where the control object has to be reached
+ * \param ctlModel the control model used by the controllable data object
+ * \param controlObjectSpec specification of the controllable data objects - used to derive required information to handle the control object
+ */
+LIB61850_API ControlObjectClient
+ControlObjectClient_createEx(const char* objectReference, IedConnection connection, ControlModel ctlModel, MmsVariableSpecification* controlObjectSpec);
+
+/**
+ * \brief Destroy the client control object instance and release all related resources
+ *
+ * \param self the control object instance to use
+ */
+LIB61850_API void
+ControlObjectClient_destroy(ControlObjectClient self);
+
+/**
+ * Cause of the \ref ControlObjectClient_ControlActionHandler invocation
+ */
+typedef enum
+{
+    CONTROL_ACTION_TYPE_SELECT = 0, /** < callback was invoked because of a select command */
+    CONTROL_ACTION_TYPE_OPERATE = 1,  /** < callback was invoked because of an operate command */
+    CONTROL_ACTION_TYPE_CANCEL = 2 /** < callback was invoked because of a cancel command */
+} ControlActionType;
+
+/**
+ * \brief 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.
+ *
+ * NOTE: Do not call \ref ControlObjectClient_destroy inside of this callback! Doing so will cause a dead-lock.
+ *
+ * \param invokeId invoke ID of the command sent by the client
+ * \param parameter the user parameter that is passed to the callback function
+ * \param err the error code when an error occurred, or IED_ERROR_OK
+ * \param type control action type that caused the callback
+ * \param success true, when the command was successful, false otherwise
+ *
+ */
+typedef void
+(*ControlObjectClient_ControlActionHandler) (uint32_t invokeId, void* parameter, IedClientError err, ControlActionType type, bool success);
+
+/**
+ * \brief Get the object reference of the control data object
+ *
+ * \param self the control object instance to use
+ *
+ * \return the object reference (string is valid only as long as the \ref ControlObjectClient instance exists).
+ */
+LIB61850_API const char*
+ControlObjectClient_getObjectReference(ControlObjectClient self);
+
+/**
+ * \brief Get the current control model (local representation) applied to the control object
+ *
+ * \param self the control object instance to use
+ *
+ * \return the current applied control model (\ref ControlModel)
+ */
+LIB61850_API ControlModel
+ControlObjectClient_getControlModel(ControlObjectClient self);
+
+/**
+ * \brief Set the applied control model
+ *
+ * NOTE: This function call will not change the server control model.
+ *
+ * \param self the control object instance to use
+ * \param ctlModel the new control model to apply
+ */
+LIB61850_API void
+ControlObjectClient_setControlModel(ControlObjectClient self, ControlModel ctlModel);
+
+/**
+ * \brief Change the control model of the server.
+ *
+ * NOTE: Not supported by all servers. Information can be found in the PIXIT of the server.
+ * Also sets the applied control model for this client control instance.
+ *
+ * \param self the control object instance to use
+ * \param ctlModel the new control model
+ */
+LIB61850_API void
+ControlObjectClient_changeServerControlModel(ControlObjectClient self, ControlModel ctlModel);
+
+/**
+ * \brief Get the type of ctlVal.
+ *
+ * This type is required for the ctlVal parameter of the \ref ControlObjectClient_operate
+ * and \ref  ControlObjectClient_selectWithValue functions.
+ *
+ * \param self the control object instance to use
+ *
+ * \return MmsType required for the ctlVal value.
+ */
+LIB61850_API MmsType
+ControlObjectClient_getCtlValType(ControlObjectClient self);
+
+/**
+ * \brief Get the error code of the last synchronous control action (operate, select, select-with-value, cancel)
+ *
+ * \param self the control object instance to use
+ *
+ * \return the client error code
+ */
+LIB61850_API IedClientError
+ControlObjectClient_getLastError(ControlObjectClient self);
+
+/**
+ * \brief Send an operate command to the server
+ *
+ * \param self the control object instance to use
+ * \param ctlVal the control value (for APC the value may be either AnalogueValue (MMS_STRUCT) or MMS_FLOAT/MMS_INTEGER
+ * \param operTime the time when the command has to be executed (for time activated control). The value represents the local time of the
+ *                 server in milliseconds since epoch. If this value is 0 the command will be executed instantly.
+ *
+ * \return true if operation has been successful, false otherwise.
+ */
+LIB61850_API bool
+ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t operTime);
+
+/**
+ * \brief Send a select command to the server
+ *
+ * The select command is only used for the control model "select-before-operate with normal security"
+ * (CONTROL_MODEL_SBO_NORMAL). The select command has to be sent before the operate command can be used.
+ *
+ * \param self the control object instance to use
+ *
+ * \return true if operation has been successful, false otherwise.
+ */
+LIB61850_API bool
+ControlObjectClient_select(ControlObjectClient self);
+
+/**
+ * \brief Send an select with value command to the server
+ *
+ * The select-with-value command is only used for the control model "select-before-operate with enhanced security"
+ * (CONTROL_MODEL_SBO_ENHANCED). The select-with-value command has to be sent before the operate command can be used.
+ *
+ * \param self the control object instance to use
+ * \param ctlVal the control value (for APC the value may be either AnalogueValue (MMS_STRUCT) or MMS_FLOAT/MMS_INTEGER
+ *
+ * \return true if select has been successful, false otherwise.
+ */
+LIB61850_API bool
+ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal);
+
+/**
+ * \brief Send a cancel command to the server
+ *
+ * The cancel command can be used to stop an ongoing operation (when the server and application
+ * support this) and to cancel a former select command.
+ *
+ * \param self the control object instance to use
+ *
+ * \return true if operation has been successful, false otherwise.
+ */
+LIB61850_API bool
+ControlObjectClient_cancel(ControlObjectClient self);
+
+
+/**
+ * \brief Send an operate command to the server - async version
+ *
+ * \param self the control object instance to use
+ * \param[out] err error code
+ * \param ctlVal the control value (for APC the value may be either AnalogueValue (MMS_STRUCT) or MMS_FLOAT/MMS_INTEGER
+ * \param operTime the time when the command has to be executed (for time activated control). The value represents the local time of the
+ *                 server in milliseconds since epoch. If this value is 0 the command will be executed instantly.
+ * \param handler the user provided callback handler
+ * \param parameter user provided parameter that is passed to the callback handler
+ *
+ * \return the invoke ID of the request
+ */
+LIB61850_API uint32_t
+ControlObjectClient_operateAsync(ControlObjectClient self, IedClientError* err, MmsValue* ctlVal, uint64_t operTime,
+        ControlObjectClient_ControlActionHandler handler, void* parameter);
+
+/**
+ * \brief Send a select command to the server - async version
+ *
+ * The select command is only used for the control model "select-before-operate with normal security"
+ * (CONTROL_MODEL_SBO_NORMAL). The select command has to be sent before the operate command can be used.
+ *
+ * \param self the control object instance to use
+ * \param[out] err error code
+ * \param handler the user provided callback handler
+ * \param parameter user provided parameter that is passed to the callback handler
+ *
+ * \return the invoke ID of the request
+ */
+LIB61850_API uint32_t
+ControlObjectClient_selectAsync(ControlObjectClient self, IedClientError* err, ControlObjectClient_ControlActionHandler handler, void* parameter);
+
+/**
+ * \brief Send a select-with-value command to the server - async version
+ *
+ * The select-with-value command is only used for the control model "select-before-operate with enhanced security"
+ * (CONTROL_MODEL_SBO_ENHANCED). The select-with-value command has to be sent before the operate command can be used.
+ *
+ * \param self the control object instance to use
+ * \param[out] err error code
+ * \param ctlVal the control value (for APC the value may be either AnalogueValue (MMS_STRUCT) or MMS_FLOAT/MMS_INTEGER
+ * \param handler the user provided callback handler
+ * \param parameter user provided parameter that is passed to the callback handler
+ *
+ * \return the invoke ID of the request
+ */
+LIB61850_API uint32_t
+ControlObjectClient_selectWithValueAsync(ControlObjectClient self, IedClientError* err, MmsValue* ctlVal,
+        ControlObjectClient_ControlActionHandler handler, void* parameter);
+
+/**
+ * \brief Send a cancel command to the server - async version
+ *
+ * The cancel command can be used to stop an ongoing operation (when the server and application
+ * support this) and to cancel a former select command.
+ *
+ * \param self the control object instance to use
+ * \param[out] err error code
+ * \param handler the user provided callback handler
+ * \param parameter user provided parameter that is passed to the callback handler
+ *
+ * \return the invoke ID of the request
+ */
+LIB61850_API uint32_t
+ControlObjectClient_cancelAsync(ControlObjectClient self, IedClientError* err, ControlObjectClient_ControlActionHandler handler, void* parameter);
+
+/**
+ * \brief Get the last received control application error
+ *
+ * NOTE: this is the content of the "LastApplError" message received from the server.
+ *
+ * \return the value of the last received application error
+ */
+LIB61850_API LastApplError
+ControlObjectClient_getLastApplError(ControlObjectClient self);
+
+/**
+ * \brief Send commands in test mode.
+ *
+ * When the server supports test mode the commands that are sent with the test flag set
+ * are not executed (will have no effect on the attached physical process).
+ *
+ * \param self the control object instance to use
+ * \param value value if the test flag (true = test mode).
+ */
+LIB61850_API void
+ControlObjectClient_setTestMode(ControlObjectClient self, bool value);
+
+/**
+ * \brief Set the origin parameter for control commands
+ *
+ * The origin parameter is used to identify the client/application that sent a control
+ * command. It is intended for later analysis.
+ *
+ * \param self the ControlObjectClient instance
+ * \param orIdent originator identification can be an arbitrary string
+ * \param orCat originator category (see \ref ORIGINATOR_CATEGORIES)
+ */
+LIB61850_API void
+ControlObjectClient_setOrigin(ControlObjectClient self, const char* orIdent, int orCat);
+
+/**
+ * \brief Use a constant T parameter for all command (select, operate, cancel) of a single control sequence
+ *
+ * NOTE: Some non-standard compliant servers may require this to accept oper/cancel requests
+ *
+ * \param self the ControlObjectClient instance
+ * \param useContantT enable this behavior with true, disable with false
+ */
+LIB61850_API void
+ControlObjectClient_useConstantT(ControlObjectClient self, bool useConstantT);
+
+/**
+ * \deprecated use ControlObjectClient_setInterlockCheck instead
+ */
+LIB61850_API DEPRECATED void
+ControlObjectClient_enableInterlockCheck(ControlObjectClient self);
+
+/**
+ * \deprecated use ControlObjectClient_setSynchroCheck instead
+ */
+LIB61850_API DEPRECATED void
+ControlObjectClient_enableSynchroCheck(ControlObjectClient self);
+
+/**
+ * \deprecated Do not use (ctlNum is handled automatically by the library)! Intended for test purposes only.
+ */
+LIB61850_API DEPRECATED void
+ControlObjectClient_setCtlNum(ControlObjectClient self, uint8_t ctlNum);
+
+/**
+ * \brief Set the value of the interlock check flag when a control command is sent
+ *
+ * \param self the ControlObjectClient instance
+ * \param value if true the server will perform a interlock check if supported
+ */
+LIB61850_API void
+ControlObjectClient_setInterlockCheck(ControlObjectClient self, bool value);
+
+/**
+ * \brief Set the value of the synchro check flag when a control command is sent
+ *
+ * \param self the ControlObjectClient instance
+ * \param value if true the server will perform a synchro check if supported
+ */
+LIB61850_API void
+ControlObjectClient_setSynchroCheck(ControlObjectClient self, bool value);
+
+
+/**
+ * \brief 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
+ * \ref ControlObjectClient_getLastApplError function.
+ *
+ * In case of CommandTermination+ the return value
+ * of \ref ControlObjectClient_getLastApplError has error=CONTROL_ERROR_NO_ERROR and
+ * addCause=ADD_CAUSE_UNKNOWN set. When addCause is different from ADD_CAUSE_UNKNOWN then the client
+ * received a CommandTermination- message.
+ *
+ * NOTE: Do not call \ref ControlObjectClient_destroy inside of this callback! Doing so will cause a dead-lock.
+ *
+ * \param parameter the user parameter that is passed to the callback function
+ * \param controlClient the ControlObjectClient instance
+ */
+typedef void (*CommandTerminationHandler) (void* parameter, ControlObjectClient controlClient);
+
+/**
+ * \brief Set the command termination callback handler for this control object
+ *
+ * This callback is invoked whenever a CommandTermination+ or CommandTermination- message is received.
+ * To distinguish between a CommandTermination+ and CommandTermination- please use the
+ * \ref ControlObjectClient_getLastApplError function. In case of CommandTermination+ the return value
+ * of \ref ControlObjectClient_getLastApplError has error=CONTROL_ERROR_NO_ERROR and
+ * addCause=ADD_CAUSE_UNKNOWN set. When addCause is different from ADD_CAUSE_UNKNOWN then the client
+ * received a CommandTermination- message.
+ *
+ * \param self the ControlObjectClient instance
+ * \param handler the callback function to be used
+ * \param handlerParameter a user parameter that is passed to the handler
+ */
+LIB61850_API void
+ControlObjectClient_setCommandTerminationHandler(ControlObjectClient self, CommandTerminationHandler handler,
+        void* handlerParameter);
+
+/** @} */
+
+/*************************************
+ * Model discovery services
+ ************************************/
+
+/**
+ * @defgroup IEC61850_CLIENT_MODEL_DISCOVERY Model discovery services
+ *
+ * @{
+ */
+
+/**
+ * \brief Retrieve the device model from the server
+ *
+ * This function retrieves the complete device model from the server. The model is buffered an can be browsed
+ * by subsequent API calls. This API call is mapped to multiple ACSI services.
+ *
+ * \param self the connection object
+ * \param error the error code if an error occurs
+ *
+ */
+LIB61850_API void
+IedConnection_getDeviceModelFromServer(IedConnection self, IedClientError* error);
+
+/**
+ * \brief Get the list of logical devices available at the server (DEPRECATED)
+ *
+ * This function is mapped to the GetServerDirectory(LD) ACSI service.
+ *
+ * NOTE: This function will call \ref IedConnection_getDeviceModelFromServer if no buffered data model
+ * information is available. Otherwise it will use the buffered information.
+ *
+ * \param self the connection object
+ * \param error the error code if an error occurs
+ *
+ * \return LinkedList with string elements representing the logical device names
+ */
+LIB61850_API LinkedList /*<char*>*/
+IedConnection_getLogicalDeviceList(IedConnection self, IedClientError* error);
+
+/**
+ * \brief Get the list of logical devices or files available at the server
+ *
+ * GetServerDirectory ACSI service implementation. This function will either return the list of
+ * logical devices (LD) present at the server or the list of available files.
+ *
+ * NOTE: When getFIleNames is false zhis function will call
+ * \ref IedConnection_getDeviceModelFromServer if no buffered data model
+ * information is available. Otherwise it will use the buffered information.
+ *
+ * \param self the connection object
+ * \param error the error code if an error occurs
+ * \param getFileNames get list of files instead of logical device names (TO BE IMPLEMENTED)
+ *
+ * \return LinkedList with string elements representing the logical device names or file names
+ */
+LIB61850_API LinkedList /*<char*>*/
+IedConnection_getServerDirectory(IedConnection self, IedClientError* error, bool getFileNames);
+
+/**
+ * \brief Get the list of logical nodes (LN) of a logical device
+ *
+ * GetLogicalDeviceDirectory ACSI service implementation. Returns the list of logical nodes names present
+ * in a logical device. The list is returned as a linked list of type LinkedList with c style string elements.
+ *
+ * NOTE: This function will call \ref IedConnection_getDeviceModelFromServer if no buffered data model
+ * information is available. Otherwise it will use the buffered information.
+ *
+ * \param self the connection object
+ * \param error the error code if an error occurs
+ * \param logicalDeviceName the name of the logical device (LD) of interest
+ *
+ * \return  LinkedList with string elements representing the logical node names
+ */
+LIB61850_API LinkedList /*<char*>*/
+IedConnection_getLogicalDeviceDirectory(IedConnection self, IedClientError* error, const char* logicalDeviceName);
+
+/**
+ * \brief returns a list of all MMS variables that are children of the given logical node
+ *
+ * This function cannot be mapped to any ACSI service. It is a convenience function for generic clients that
+ * wants to show a list of all available children of the MMS named variable representing the logical node.
+ *
+ * NOTE: This function will call \ref IedConnection_getDeviceModelFromServer if no buffered data model
+ * information is available. Otherwise it will use the buffered information.
+ *
+ * \param self the connection object
+ * \param error the error code if an error occurs
+ * \param logicalNodeReference string that represents the LN reference
+ *
+ * \return the list of all MMS named variables as C strings in a LinkedList type
+ */
+LIB61850_API LinkedList /*<char*>*/
+IedConnection_getLogicalNodeVariables(IedConnection self, IedClientError* error,
+		const char* logicalNodeReference);
+
+/**
+ * \brief returns the directory of the given logical node (LN) containing elements of the specified ACSI class
+ *
+ * Implementation of the GetLogicalNodeDirectory ACSI service. In contrast to the ACSI description this
+ * function does not always creates a request to the server. For most ACSI classes it simply accesses the
+ * data model that was retrieved before or calls \ref IedConnection_getDeviceModelFromServer if no buffered data model
+ * information is available. An exception to this rule are the ACSI classes ACSI_CLASS_DATASET and
+ * ACSI_CLASS_LOG. Both always perform a request to the server.
+ *
+ * \param self the connection object
+ * \param error the error code if an error occurs
+ * \param logicalNodeReference string that represents the LN reference
+ * \param acsiClass specifies the ACSI class
+ *
+ * \return list of all logical node elements of the specified ACSI class type as C strings in a LinkedList
+ */
+LIB61850_API LinkedList /*<char*>*/
+IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error,
+		const char* logicalNodeReference, ACSIClass acsiClass);
+
+/**
+ * \brief returns the directory of the given data object (DO)
+ *
+ * Implementation of the GetDataDirectory ACSI service. This will return the list of
+ * all data attributes or sub data objects.
+ *
+ * NOTE: This function will call \ref IedConnection_getDeviceModelFromServer if no buffered data model
+ * information is available. Otherwise it will use the buffered information.
+ *
+ * \param self the connection object
+ * \param error the error code if an error occurs
+ * \param dataReference string that represents the DO reference
+ *
+ * \return list of all data attributes or sub data objects as C strings in a LinkedList
+ */
+LIB61850_API LinkedList /*<char*>*/
+IedConnection_getDataDirectory(IedConnection self, IedClientError* error, const char* dataReference);
+
+/**
+ * \brief returns the directory of the given data object (DO)
+ *
+ * Implementation of the GetDataDirectory ACSI service. This will return the list of
+ * C strings with all data attributes or sub data objects as elements. The returned
+ * strings will contain the functional constraint appended in square brackets when appropriate.
+ *
+ * NOTE: This function will call \ref IedConnection_getDeviceModelFromServer if no buffered data model
+ * information is available. Otherwise it will use the buffered information.
+ *
+ * \param self the connection object
+ * \param error the error code if an error occurs
+ * \param dataReference string that represents the DO reference
+ *
+ * \return list of all data attributes or sub data objects as C strings in a LinkedList
+ */
+LIB61850_API LinkedList /*<char*>*/
+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.
+ *
+ * NOTE: This function will call \ref IedConnection_getDeviceModelFromServer if no buffered data model
+ * information is available. Otherwise it will use the buffered information.
+ *
+ * WARNING: Starting with version 1.0.3 the functional constraint will no longer be appended to
+ * the name string.
+ *
+ * \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
+ */
+LIB61850_API 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.
+ *
+ * This function can be used to get the MMS variable type specification for an IEC 61850 data attribute. It is an extension
+ * of the ACSI that may be required by generic client applications.
+ *
+ * NOTE: API user is responsible to free the resources (see \ref MmsVariableSpecification_destroy)
+ *
+ * \param self the connection object
+ * \param error the error code if an error occurs
+ * \param dataAttributeReference string that represents the DA reference
+ * \param fc functional constraint of the DA
+ *
+ * \return MmsVariableSpecification of the data attribute.
+ */
+LIB61850_API MmsVariableSpecification*
+IedConnection_getVariableSpecification(IedConnection self, IedClientError* error, const char* dataAttributeReference,
+        FunctionalConstraint fc);
+
+/**
+ * \brief Get all variables of the logical device
+ *
+ * NOTE: This function will return all MMS variables of the logical device (MMS domain). The result will be in the
+ * MMS notation (like "GGIO1$ST$Ind1$stVal") and also contain the variables of control blocks.
+ *
+ * \param[in] self the connection object
+ * \param[out] error the error code if an error occurs
+ * \param[in] ldName the logical device name
+ *
+ * \return a \ref LinkedList with the MMS variable names as string. Has to be released by the caller
+ */
+LIB61850_API LinkedList
+IedConnection_getLogicalDeviceVariables(IedConnection self, IedClientError* error, const char* ldName);
+
+/**
+ * \brief Get the data set names of the logical device
+ *
+ * NOTE: This function will return all data set names (MMS named variable lists) of the logical device (MMS domain). The result will be in the
+ * MMS notation (like "LLN0$dataset1").
+ *
+ * \param[in] self the connection object
+ * \param[out] error the error code if an error occurs
+ * \param[in] ldName the logical device name
+ *
+ * \return a \ref LinkedList with data set names as string. Has to be released by the caller.
+ */
+LIB61850_API LinkedList
+IedConnection_getLogicalDeviceDataSets(IedConnection self, IedClientError* error, const char* ldName);
+
+/*****************************************
+ * Asynchronous model discovery functions
+ *****************************************/
+
+typedef void
+(*IedConnection_GetNameListHandler) (uint32_t invokeId, void* parameter, IedClientError err, LinkedList nameList, bool moreFollows);
+
+/**
+ * \brief Get the server directory (logical devices name) - asynchronous version
+ *
+ * \param[in] self the connection object
+ * \param[out] error the error code if an error occurs
+ * \param[in] continueAfter the name of the last received element when the call is a continuation, or NULL for the first call
+ * \param[in] result list to store (append) the response names, or NULL to create a new list for the response names
+ * \param[in] handler will be called when response is received or timed out.
+ * \param[in] parameter
+ *
+ * \return the invoke ID of the request
+ */
+LIB61850_API uint32_t
+IedConnection_getServerDirectoryAsync(IedConnection self, IedClientError* error, const char* continueAfter, LinkedList result,
+        IedConnection_GetNameListHandler handler, void* parameter);
+
+/**
+ * \brief Get the variables in the logical device - asynchronous version
+ *
+ * NOTE: This function will return all MMS variables of the logical device (MMS domain). The result will be in the
+ * MMS notation (like "GGIO1$ST$Ind1$stVal") and also contain the variables of control blocks.
+ *
+ * \param[in] self the connection object
+ * \param[out] error the error code if an error occurs
+ * \param[in] ldName the logical device name
+ * \param[in] continueAfter the name of the last received element when the call is a continuation, or NULL for the first call
+ * \param[in] result list to store (append) the response names, or NULL to create a new list for the response names
+ * \param[in] handler will be called when response is received or timed out.
+ * \param[in] parameter
+ *
+ * \return the invoke ID of the request
+ */
+LIB61850_API uint32_t
+IedConnection_getLogicalDeviceVariablesAsync(IedConnection self, IedClientError* error, const char* ldName, const char* continueAfter, LinkedList result,
+        IedConnection_GetNameListHandler handler, void* parameter);
+
+/**
+ * \brief Get the data set names in the logical device - asynchronous version
+ *
+ * NOTE: This function will return all data set names (MMS named variable lists) of the logical device (MMS domain). The result will be in the
+ * MMS notation (like "LLN0$dataset1").
+ *
+ * \param[in] self the connection object
+ * \param[out] error the error code if an error occurs
+ * \param[in] ldName the logical device name
+ * \param[in] continueAfter the name of the last received element when the call is a continuation, or NULL for the first call
+ * \param[in] result list to store (append) the response names, or NULL to create a new list for the response names
+ * \param[in] handler will be called when response is received or timed out.
+ * \param[in] parameter
+ *
+ * \return the invoke ID of the request
+ */
+LIB61850_API uint32_t
+IedConnection_getLogicalDeviceDataSetsAsync(IedConnection self, IedClientError* error, const char* ldName, const char* continueAfter, LinkedList result,
+        IedConnection_GetNameListHandler handler, void* parameter);
+
+
+typedef void
+(*IedConnection_GetVariableSpecificationHandler) (uint32_t invokeId, void* parameter, IedClientError err, MmsVariableSpecification* spec);
+
+/**
+ * \brief Get the specification of a variable (data attribute or functional constraint data object) - asynchronous version
+ *
+ * \param[in] self the connection object
+ * \param[out] error the error code if an error occurs
+ * \param[in] dataAttributeReference the data attribute reference (FCDA or FCDO)
+ * \param[in] handler will be called when response is received or timed out.
+ * \param[in] parameter
+ *
+ * \return the invoke ID of the request
+ */
+LIB61850_API uint32_t
+IedConnection_getVariableSpecificationAsync(IedConnection self, IedClientError* error, const char* dataAttributeReference,
+        FunctionalConstraint fc, IedConnection_GetVariableSpecificationHandler handler, void* parameter);
+
+/** @} */
+
+/**
+ * @defgroup IEC61850_CLIENT_LOG_SERVICE Log service related functions, data types, and definitions
+ *
+ * @{
+ */
+
+/**
+ * \brief Implementation of the QueryLogByTime ACSI service
+ *
+ * Read log entries from a log at the server. The log entries to read are specified by
+ * a starting time and an end time. If the complete range does not fit in a single MMS message
+ * the moreFollows flag will be set to true, to indicate that more entries are available for the
+ * specified time range.
+ *
+ * \param self the connection object
+ * \param error the error code if an error occurs
+ * \param logReference log object reference in the form <LD name>/<LN name>$<log name>
+ * \param startTime as millisecond UTC timestamp
+ * \param endTime as millisecond UTC timestamp
+ * \param moreFollows (output value) indicates that more entries are available that match the specification.
+ *
+ * \return list of MmsJournalEntry objects matching the specification
+ */
+LIB61850_API LinkedList /* <MmsJournalEntry> */
+IedConnection_queryLogByTime(IedConnection self, IedClientError* error, const char* logReference,
+        uint64_t startTime, uint64_t endTime, bool* moreFollows);
+
+/**
+ * \brief Implementation of the QueryLogAfter ACSI service
+ *
+ * Read log entries from a log at the server following the entry with the specified entryID and timestamp.
+ * If the complete range does not fit in a single MMS message
+ * the moreFollows flag will be set to true, to indicate that more entries are available for the
+ * specified time range.
+ *
+ * \param self the connection object
+ * \param error the error code if an error occurs
+ * \param logReference log object reference in the form <LD name>/<LN name>$<log name>
+ * \param entryID usually the entryID of the last received entry
+ * \param timeStamp as millisecond UTC timestamp
+ * \param moreFollows (output value) indicates that more entries are available that match the specification.
+ *
+ * \return list of MmsJournalEntry objects matching the specification
+ */
+LIB61850_API LinkedList /* <MmsJournalEntry> */
+IedConnection_queryLogAfter(IedConnection self, IedClientError* error, const char* logReference,
+        MmsValue* entryID, uint64_t timeStamp, bool* moreFollows);
+
+
+typedef void
+(*IedConnection_QueryLogHandler) (uint32_t invokeId, void* parameter, IedClientError mmsError, LinkedList /* <MmsJournalEntry> */ journalEntries, bool moreFollows);
+
+LIB61850_API uint32_t
+IedConnection_queryLogByTimeAsync(IedConnection self, IedClientError* error, const char* logReference,
+        uint64_t startTime, uint64_t endTime, IedConnection_QueryLogHandler handler, void* parameter);
+
+LIB61850_API uint32_t
+IedConnection_queryLogAfterAsync(IedConnection self, IedClientError* error, const char* logReference,
+        MmsValue* entryID, uint64_t timeStamp, IedConnection_QueryLogHandler handler, void* parameter);
+
+/** @} */
+
+/**
+ * @defgroup IEC61850_CLIENT_FILE_SERVICE File service related functions, data types, and definitions
+ *
+ * @{
+ */
+
+typedef struct sFileDirectoryEntry* FileDirectoryEntry;
+
+/**
+ * @deprecated Will be removed from API
+ */
+LIB61850_API FileDirectoryEntry
+FileDirectoryEntry_create(const char* fileName, uint32_t fileSize, uint64_t lastModified);
+
+/**
+ * \brief Destroy a FileDirectoryEntry object (free all resources)
+ *
+ * NOTE: Usually is called as a parameter of the \ref LinkedList_destroyDeep function.
+ *
+ * \param self the FileDirectoryEntry object
+ */
+LIB61850_API void
+FileDirectoryEntry_destroy(FileDirectoryEntry self);
+
+/**
+ * \brief Get the name of the file
+ *
+ * \param self the FileDirectoryEntry object
+ *
+ * \return name of the file as null terminated string
+ */
+LIB61850_API const char*
+FileDirectoryEntry_getFileName(FileDirectoryEntry self);
+
+/**
+ * \brief Get the file size in bytes
+ *
+ * \param self the FileDirectoryEntry object
+ *
+ * \return size of the file in bytes, or 0 if file size is unknown
+ */
+LIB61850_API uint32_t
+FileDirectoryEntry_getFileSize(FileDirectoryEntry self);
+
+/**
+ * \brief Get the timestamp of last modification of the file
+ *
+ * \param self the FileDirectoryEntry object
+ *
+ * \return UTC timestamp in milliseconds
+ */
+LIB61850_API uint64_t
+FileDirectoryEntry_getLastModified(FileDirectoryEntry self);
+
+
+/**
+ * \brief returns the directory entries of the specified file directory.
+ *
+ * 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
+ *
+ * \return the list of directory entries. The return type is a LinkedList with FileDirectoryEntry elements
+ */
+LIB61850_API LinkedList /*<FileDirectoryEntry>*/
+IedConnection_getFileDirectory(IedConnection self, IedClientError* error, const char* directoryName);
+
+
+/**
+ * \brief returns the directory entries of the specified file directory returned by a single file directory request.
+ *
+ * This function will only create a single request and the result may only be the directory that fits
+ * into a single MMS PDU. If the server contains more directory entries, this will be indicated by setting
+ * the moreFollows variable (if provided by the caller). If the directory entry does not fit into a single MMS
+ * PDU the next part of the directory list can be requested by setting the continueAfter parameter with the value
+ * of the last filename of the received list.
+ *
+ * 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
+ * \param continueAfter last received filename to continue after, or NULL for the first request
+ * \param moreFollows if provided by the caller (non NULL) the function will indicate if more directory entries
+ *                    are available.
+ *
+ * \return the list of directory entries. The return type is a LinkedList with FileDirectoryEntry elements
+ */
+LIB61850_API LinkedList /*<FileDirectoryEntry>*/
+IedConnection_getFileDirectoryEx(IedConnection self, IedClientError* error, const char* directoryName, const char* continueAfter,
+        bool* moreFollows);
+
+/**
+ * \brief Callback handler for the get file directory service
+ *
+ * Will be called once for each file directory entry and after the last entry with \ref moreFollows = false to indicate
+ * to indicate that no more data will follow. In case of an error the callback will be called with
+ * \ref err != IED_ERROR_OK and moreFollows = false.
+ */
+
+/**
+ * \brief Callback handler for the get file directory service
+ *
+ * Will be called once for each file directory entry and after the last entry with \ref filename = NULL to indicate
+ * with \ref moreFollows set to true if more data is available at the server (can only happen when using the \ref IedConnection_getFileDirectoryAsyncEx
+ * function). In case of an error the callback will be called with \ref err != IED_ERROR_OK and moreFollows = false.
+ *
+ * \param invokeId invoke ID of the request
+ * \param parameter user provided parameter
+ * \param err error code in case of a problem, otherwise IED_ERROR_OK
+ * \param filename the filename of the current file directory entry or NULL if no more entries are available
+ * \param size the file size in byte of the current file directory entry
+ * \param lastModified the last modified timestamp of the current file directory entry
+ *
+ * \return return false when the request has to be stopped (no further callback invokations), true otherwise
+ */
+typedef bool
+(*IedConnection_FileDirectoryEntryHandler) (uint32_t invokeId, void* parameter, IedClientError err, char* filename, uint32_t size, uint64_t lastModfified,
+        bool moreFollows);
+
+/**
+ * \brief Get file directory (single request) - asynchronous version
+ *
+ * The provided handler will be called for each received file directory entry.
+ *
+ * NOTE: This will only cause a single MMS request. When the resulting file directory doesn't fit into
+ * a single MMS PDU another request has to be sent indicating a continuation point with the continueAfter
+ * parameter.
+ *
+ * \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
+ * \param continueAfter last received filename to continue after, or NULL for the first request
+ * \param handler the callback handler
+ * \param parameter user provided callback parameter
+ *
+ * \return the invokeId of the first file directory request
+ */
+LIB61850_API uint32_t
+IedConnection_getFileDirectoryAsyncEx(IedConnection self, IedClientError* error, const char* directoryName, const char* continueAfter,
+        IedConnection_FileDirectoryEntryHandler handler, void* parameter);
+
+/**
+ * \brief user provided handler to receive the data of the GetFile request
+ *
+ * This handler will be invoked whenever the clients receives a data block from
+ * the server. The API user has to copy the data to another location before returning.
+ * The other location could for example be a file in the clients file system.
+ *
+ * \param parameter user provided parameter
+ * \param buffer pointer to the buffer containing the received data
+ * \param bytesRead number of bytes available in the buffer
+ *
+ * \return true if the client implementation shall continue to download data false if the download
+ *         should be stopped. E.g. if the file cannot be stored client side due to missing resources.
+ */
+typedef bool
+(*IedClientGetFileHandler) (void* parameter, uint8_t* buffer, uint32_t bytesRead);
+
+/**
+ * \brief Implementation of the GetFile ACSI service
+ *
+ * Download a file from the server.
+ *
+ * \param self the connection object
+ * \param error the error code if an error occurs
+ * \param fileName the name of the file to be read from the server
+ *
+ * \return number of bytes received
+ */
+LIB61850_API uint32_t
+IedConnection_getFile(IedConnection self, IedClientError* error, const char* fileName, IedClientGetFileHandler handler,
+        void* handlerParameter);
+
+
+/**
+ * \brief User provided handler to receive the data of the asynchronous GetFile request
+ *
+ * This handler will be invoked whenever the clients receives a data block from
+ * the server. The API user has to copy the data to another location before returning.
+ * The other location could for example be a file in the clients file system. When the
+ * last data block is received the moreFollows parameter will be set to false.
+ *
+ * \param invokeId invoke ID of the message containing the received data
+ * \param parameter user provided parameter passed to the callback
+ * \param err error code in case of an error or IED_ERROR_OK
+ * \param originalInvokeId the invoke ID of the original (first) request. This is usually the request to open the file.
+ * \param buffer the buffer that contains the received file data
+ * \param bytesRead the number of bytes read into the buffer
+ * \param moreFollows indicates that more file data is following
+ *
+ * \return true, continue the file download when moreFollows is true, false, stop file download
+ */
+typedef bool
+(*IedConnection_GetFileAsyncHandler) (uint32_t invokeId, void* parameter, IedClientError err, uint32_t originalInvokeId,
+        uint8_t* buffer, uint32_t bytesRead, bool moreFollows);
+
+
+/**
+ * \brief Implementation of the GetFile ACSI service - asynchronous version
+ *
+ * Download a file from the server.
+ *
+ * NOTE: This function can cause several request messages to be sent until the complete file is received
+ * or the file transfer is canceled. It allocates a background task and an outstanding call slot.
+ *
+ * \param self the connection object
+ * \param error the error code if an error occurs
+ * \param fileName the name of the file to be read from the server
+ * \param hander callback handler that is called for each received data or error message
+ * \param parameter user provided callback parameter
+ *
+ * \return invokeId of the first sent request
+ */
+LIB61850_API uint32_t
+IedConnection_getFileAsync(IedConnection self, IedClientError* error, const char* fileName, IedConnection_GetFileAsyncHandler handler,
+        void* parameter);
+
+/**
+ * \brief Set the virtual filestore basepath for the setFile service
+ *
+ * All external file service accesses will be mapped to paths relative to the base directory.
+ * NOTE: This function is only available when the CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME
+ * option in stack_config.h is set.
+ *
+ * \param self the connection object
+ * \param basepath the new virtual filestore basepath
+ */
+LIB61850_API void
+IedConnection_setFilestoreBasepath(IedConnection, const char* basepath);
+
+/**
+ * \brief Implementation of the SetFile ACSI service
+ *
+ * Upload a file to the server. The file has to be available in the local VMD filestore.
+ *
+ * \param self the connection object
+ * \param error the error code if an error occurs
+ * \param sourceFilename the filename of the local (client side) file
+ * \param destinationFilename the filename of the remote (service side) file
+ */
+LIB61850_API void
+IedConnection_setFile(IedConnection self, IedClientError* error, const char* sourceFilename, const char* destinationFilename);
+
+/**
+ * \brief Implementation of the SetFile ACSI service - asynchronous version
+ *
+ * Upload a file to the server. The file has to be available in the local VMD filestore.
+ *
+ * \param self the connection object
+ * \param error the error code if an error occurs
+ * \param sourceFilename the filename of the local (client side) file
+ * \param destinationFilename the filename of the remote (service side) file
+ * \param handler callback handler that is called when the obtain file response has been received
+ * \param parameter user provided callback parameter
+ */
+LIB61850_API uint32_t
+IedConnection_setFileAsync(IedConnection self, IedClientError* error, const char* sourceFilename, const char* destinationFilename,
+        IedConnection_GenericServiceHandler handler, void* parameter);
+
+/**
+ * \brief Implementation of the DeleteFile ACSI service
+ *
+ * Delete a file at the server.
+ *
+ * \param self the connection object
+ * \param error the error code if an error occurs
+ * \param fileName the name of the file to delete
+ */
+LIB61850_API void
+IedConnection_deleteFile(IedConnection self, IedClientError* error, const char* fileName);
+
+/**
+ * \brief Implementation of the DeleteFile ACSI service - asynchronous version
+ *
+ * Delete a file at the server.
+ *
+ * \param self the connection object
+ * \param error the error code if an error occurs
+ * \param fileName the name of the file to delete
+ * \param handler callback handler that is called when the obtain file response has been received
+ * \param parameter user provided callback parameter
+ */
+LIB61850_API uint32_t
+IedConnection_deleteFileAsync(IedConnection self, IedClientError* error, const char* fileName,
+        IedConnection_GenericServiceHandler handler, void* parameter);
+
+
+/** @} */
+
+/**@}*/
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* IEC61850_CLIENT_H_ */

+ 553 - 0
library/include/libiec61850/iec61850_common.h

@@ -0,0 +1,553 @@
+/*
+ *  iec61850_common.h
+ *
+ *  Copyright 2013-2019 Michael Zillgith
+ *
+ *  This file is part of libIEC61850.
+ *
+ *  libIEC61850 is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  libIEC61850 is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with libIEC61850.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef IEC61850_COMMON_H_
+#define IEC61850_COMMON_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#include "libiec61850_common_api.h"
+#include "logging_api.h"
+#include "linked_list.h"
+
+/**
+ * @defgroup iec61850_common_api_group IEC 61850 API common parts
+ */
+/**@{*/
+
+/** IEC 61850 edition 1 */
+#define IEC_61850_EDITION_1   0
+
+/** IEC 61850 edition 2 */
+#define IEC_61850_EDITION_2   1
+
+/** IEC 61850 edition 2.1 */
+#define IEC_61850_EDITION_2_1 2
+
+/** PhyComAddress type contains Ethernet address and VLAN attributes */
+typedef struct {
+    uint8_t vlanPriority;
+    uint16_t vlanId;
+    uint16_t appId;
+    uint8_t dstAddress[6];
+} PhyComAddress;
+
+/** IEC 61850 ACSI classes */
+typedef enum {
+    ACSI_CLASS_DATA_OBJECT,
+    ACSI_CLASS_DATA_SET,
+    ACSI_CLASS_BRCB,
+    ACSI_CLASS_URCB,
+    ACSI_CLASS_LCB,
+    ACSI_CLASS_LOG,
+    ACSI_CLASS_SGCB,
+    ACSI_CLASS_GoCB,
+    ACSI_CLASS_GsCB,
+    ACSI_CLASS_MSVCB,
+    ACSI_CLASS_USVCB
+} ACSIClass;
+
+/**
+ * \brief Control model (represented by "ctlModel" attribute)
+ */
+typedef enum {
+    /**
+     * No support for control functions. Control object only support status information.
+     */
+    CONTROL_MODEL_STATUS_ONLY = 0,
+
+    /**
+     * Direct control with normal security: Supports Operate, TimeActivatedOperate (optional),
+     * and Cancel (optional).
+     */
+    CONTROL_MODEL_DIRECT_NORMAL = 1,
+
+    /**
+     * Select before operate (SBO) with normal security: Supports Select, Operate, TimeActivatedOperate (optional),
+     * and Cancel (optional).
+     */
+    CONTROL_MODEL_SBO_NORMAL = 2,
+
+    /**
+     * Direct control with enhanced security (enhanced security includes the CommandTermination service)
+     */
+    CONTROL_MODEL_DIRECT_ENHANCED = 3,
+
+    /**
+     * Select before operate (SBO) with enhanced security (enhanced security includes the CommandTermination service)
+     */
+    CONTROL_MODEL_SBO_ENHANCED = 4
+} ControlModel;
+
+/**
+ * @defgroup TRIGGER_OPTIONS Trigger options (bit values combinable)
+ *
+ * @{
+ */
+
+/** Report will be triggered when data changes */
+#define TRG_OPT_DATA_CHANGED 1
+
+/** Report will be triggered when quality changes */
+#define TRG_OPT_QUALITY_CHANGED 2
+
+/** Report will be triggered when data is updated */
+#define TRG_OPT_DATA_UPDATE 4
+
+/** Report will be triggered periodically */
+#define TRG_OPT_INTEGRITY 8
+
+/** Report will be triggered by GI (general interrogation) request */
+#define TRG_OPT_GI 16
+
+/** Report will be triggered only on rising edge (transient variable */
+#define TRG_OPT_TRANSIENT 128
+/** @} */
+
+
+
+/**
+ * @defgroup REPORT_OPTIONS Report options (bit values combinable)
+ *
+ * @{
+ */
+
+/** Report contains sequence number */
+#define RPT_OPT_SEQ_NUM 1
+
+/** Report contains a report timestamp */
+#define RPT_OPT_TIME_STAMP 2
+
+/** Report contains reason for inclusion value for each included data set member */
+#define RPT_OPT_REASON_FOR_INCLUSION 4
+
+/** Report contains data set object reference */
+#define RPT_OPT_DATA_SET 8
+
+/** Report contains data reference for each included data set member */
+#define RPT_OPT_DATA_REFERENCE 16
+
+/** Report contains buffer overflow flag */
+#define RPT_OPT_BUFFER_OVERFLOW 32
+
+/** Report contains entry id */
+#define RPT_OPT_ENTRY_ID 64
+
+/** Report contains configuration revision */
+#define RPT_OPT_CONF_REV 128
+/** @} */
+
+/**
+ * @defgroup ORIGINATOR_CATEGORIES Originator categories (orCat)
+ *
+ * @{
+ */
+
+/** Not supported - should not be used */
+#define CONTROL_ORCAT_NOT_SUPPORTED 0
+
+/** Control operation issued from an operator using a client located at bay level */
+#define CONTROL_ORCAT_BAY_CONTROL 1
+
+/** Control operation issued from an operator using a client located at station level */
+#define CONTROL_ORCAT_STATION_CONTROL 2
+
+/** Control operation from a remote operator outside the substation (for example network control center) */
+#define CONTROL_ORCAT_REMOTE_CONTROL 3
+
+/** Control operation issued from an automatic function at bay level */
+#define CONTROL_ORCAT_AUTOMATIC_BAY 4
+
+/** Control operation issued from an automatic function at station level */
+#define CONTROL_ORCAT_AUTOMATIC_STATION 5
+
+/** Control operation issued from a automatic function outside of the substation */
+#define CONTROL_ORCAT_AUTOMATIC_REMOTE 6
+
+/** Control operation issued from a maintenance/service tool */
+#define CONTROL_ORCAT_MAINTENANCE 7
+
+/** Status change occurred without control action (for example external trip of a circuit breaker or failure inside the breaker) */
+#define CONTROL_ORCAT_PROCESS 8
+
+/** @} */
+
+/**
+ * @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 CONTROL_LAST_APPL_ERROR Definition for LastAppError error type - used in control models
+ *
+ * @{
+ */
+
+typedef enum {
+    CONTROL_ERROR_NO_ERROR = 0,
+    CONTROL_ERROR_UNKNOWN = 1,
+    CONTROL_ERROR_TIMEOUT_TEST = 2,
+    CONTROL_ERROR_OPERATOR_TEST = 3
+} ControlLastApplError;
+
+/** @} */
+
+/**
+ * @defgroup FUNCTIONAL_CONSTRAINTS Definitions and functions related to functional constraints (FCs)
+ *
+ * @{
+ */
+
+/** FCs (Functional constraints) according to IEC 61850-7-2 */
+typedef enum eFunctionalConstraint {
+    /** Status information */
+    IEC61850_FC_ST = 0,
+    /** Measurands - analog values */
+    IEC61850_FC_MX = 1,
+    /** Setpoint */
+    IEC61850_FC_SP = 2,
+    /** Substitution */
+    IEC61850_FC_SV = 3,
+    /** Configuration */
+    IEC61850_FC_CF = 4,
+    /** Description */
+    IEC61850_FC_DC = 5,
+    /** Setting group */
+    IEC61850_FC_SG = 6,
+    /** Setting group editable */
+    IEC61850_FC_SE = 7,
+    /** Service response / Service tracking */
+    IEC61850_FC_SR = 8,
+    /** Operate received */
+    IEC61850_FC_OR = 9,
+    /** Blocking */
+    IEC61850_FC_BL = 10,
+    /** Extended definition */
+    IEC61850_FC_EX = 11,
+    /** Control */
+    IEC61850_FC_CO = 12,
+    /** Unicast SV */
+    IEC61850_FC_US = 13,
+    /** Multicast SV */
+    IEC61850_FC_MS = 14,
+    /** Unbuffered report */
+    IEC61850_FC_RP = 15,
+    /** Buffered report */
+    IEC61850_FC_BR = 16,
+    /** Log control blocks */
+    IEC61850_FC_LG = 17,
+    /** Goose control blocks */
+    IEC61850_FC_GO = 18,
+
+    /** All FCs - wildcard value */
+    IEC61850_FC_ALL = 99,
+    IEC61850_FC_NONE = -1
+} FunctionalConstraint;
+
+/**extern "C" {
+ * \brief convert a function constraint to a static string
+ */
+LIB61850_API char*
+FunctionalConstraint_toString(FunctionalConstraint fc);
+
+/**
+ * \brief parse a string treated as a functional constraint representation
+ */
+LIB61850_API FunctionalConstraint
+FunctionalConstraint_fromString(const char* fcString);
+
+/** @} */
+
+/**
+ * @defgroup QUALITY Definitions and functions related to data attribute quality
+ *
+ * @{
+ */
+
+
+typedef uint16_t Quality;
+typedef uint16_t Validity;
+
+#define QUALITY_VALIDITY_GOOD 0
+#define QUALITY_VALIDITY_INVALID 2
+#define QUALITY_VALIDITY_RESERVED 1
+#define QUALITY_VALIDITY_QUESTIONABLE 3
+
+#define QUALITY_DETAIL_OVERFLOW 4
+#define QUALITY_DETAIL_OUT_OF_RANGE 8
+#define QUALITY_DETAIL_BAD_REFERENCE 16
+#define QUALITY_DETAIL_OSCILLATORY 32
+#define QUALITY_DETAIL_FAILURE 64
+#define QUALITY_DETAIL_OLD_DATA 128
+#define QUALITY_DETAIL_INCONSISTENT 256
+#define QUALITY_DETAIL_INACCURATE 512
+
+#define QUALITY_SOURCE_SUBSTITUTED 1024
+
+#define QUALITY_TEST 2048
+
+#define QUALITY_OPERATOR_BLOCKED  4096
+
+#define QUALITY_DERIVED 8192
+
+LIB61850_API Validity
+Quality_getValidity(Quality* self);
+
+LIB61850_API void
+Quality_setValidity(Quality* self, Validity validity);
+
+LIB61850_API void
+Quality_setFlag(Quality* self, int flag);
+
+LIB61850_API void
+Quality_unsetFlag(Quality* self, int flag);
+
+LIB61850_API bool
+Quality_isFlagSet(Quality* self, int flag);
+
+LIB61850_API Quality
+Quality_fromMmsValue(const MmsValue* mmsValue);
+
+LIB61850_API MmsValue*
+Quality_toMmsValue(Quality* self, 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
+ */
+LIB61850_API Dbpos
+Dbpos_fromMmsValue(const 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
+ */
+LIB61850_API MmsValue*
+Dbpos_toMmsValue(MmsValue* mmsValue, Dbpos dbpos);
+
+/** @} */
+
+/**
+ * @defgroup TIMESTAMP Definitions and functions related to IEC 61850 Timestamp (UTC Time) data type
+ *
+ * @{
+ */
+
+typedef union {
+    uint8_t val[8];
+} Timestamp;
+
+LIB61850_API Timestamp*
+Timestamp_create(void);
+
+LIB61850_API Timestamp*
+Timestamp_createFromByteArray(const uint8_t* byteArray);
+
+LIB61850_API void
+Timestamp_destroy(Timestamp* self);
+
+LIB61850_API void
+Timestamp_clearFlags(Timestamp* self);
+
+LIB61850_API uint32_t
+Timestamp_getTimeInSeconds(Timestamp* self);
+
+LIB61850_API msSinceEpoch
+Timestamp_getTimeInMs(Timestamp* self);
+
+LIB61850_API nsSinceEpoch
+Timestamp_getTimeInNs(Timestamp* self);
+
+LIB61850_API bool
+Timestamp_isLeapSecondKnown(Timestamp* self);
+
+LIB61850_API void
+Timestamp_setLeapSecondKnown(Timestamp* self, bool value);
+
+LIB61850_API bool
+Timestamp_hasClockFailure(Timestamp* self);
+
+LIB61850_API void
+Timestamp_setClockFailure(Timestamp* self, bool value);
+
+LIB61850_API bool
+Timestamp_isClockNotSynchronized(Timestamp* self);
+
+LIB61850_API void
+Timestamp_setClockNotSynchronized(Timestamp* self, bool value);
+
+LIB61850_API int
+Timestamp_getSubsecondPrecision(Timestamp* self);
+
+LIB61850_API void
+Timestamp_setFractionOfSecondPart(Timestamp* self, uint32_t fractionOfSecond);
+
+LIB61850_API uint32_t
+Timestamp_getFractionOfSecondPart(Timestamp* self);
+
+LIB61850_API float
+Timestamp_getFractionOfSecond(Timestamp* self);
+
+/**
+ * \brief Set the subsecond precision value of the time stamp
+ *
+ * \param subsecondPrecision the number of significant bits of the fractionOfSecond part of the time stamp
+ */
+LIB61850_API void
+Timestamp_setSubsecondPrecision(Timestamp* self, int subsecondPrecision);
+
+/**
+ * \brief Set the time in seconds
+ *
+ * NOTE: the fractionOfSecond part is set to zero
+ * NOTE: the subSecondPrecision is not touched
+ *
+ * \param self the Timestamp instance
+ * \param secondsSinceEpoch the seconds since unix epoch (unix timestamp)
+ */
+LIB61850_API void
+Timestamp_setTimeInSeconds(Timestamp* self, uint32_t secondsSinceEpoch);
+
+/**
+ * \brief Set the time in milliseconds
+ *
+ * NOTE: the subSecondPrecision is not touched
+ *
+ * \param self the Timestamp instance
+ * \param msTime the milliseconds since unix epoch
+ */
+LIB61850_API void
+Timestamp_setTimeInMilliseconds(Timestamp* self, msSinceEpoch msTime);
+
+/**
+ * \brief Set the time in nanoseconds
+ *
+ * NOTE: the subSecondPrecision is not touched
+ *
+ * \param self the Timestamp instance
+ * \param msTime the nanoseconds since unix epoch
+ */
+LIB61850_API void
+Timestamp_setTimeInNanoseconds(Timestamp* self, nsSinceEpoch nsTime);
+
+LIB61850_API void
+Timestamp_setByMmsUtcTime(Timestamp* self, const MmsValue* mmsValue);
+
+/**
+ * \brief Set an MmsValue instance of type UTCTime to the timestamp value
+ *
+ * \param self the Timestamp instance
+ * \param mmsValue the mmsValue instance, if NULL a new instance will be created
+ */
+LIB61850_API MmsValue*
+Timestamp_toMmsValue(Timestamp* self, MmsValue* mmsValue);
+
+/**
+ * \brief Get the Timestamp value from an MmsValue instance of type MMS_UTC_TIME
+ *
+ * \param self the Timestamp instance or NULL to create a new instance
+ * \param mmsValue the mmsValue instance of type MMS_UTC_TIME
+ *
+ * \return the updated Timestamp value or NULL in case of an error
+ */
+LIB61850_API Timestamp*
+Timestamp_fromMmsValue(Timestamp* self, MmsValue* mmsValue);
+
+/**
+ * \brief Get the version of the library as string
+ *
+ * \return the version of the library (e.g. "1.2.2")
+ */
+LIB61850_API char*
+LibIEC61850_getVersionString(void);
+
+/** @} */
+
+/**@}*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* IEC61850_COMMON_H_ */

+ 66 - 0
library/include/libiec61850/iec61850_config_file_parser.h

@@ -0,0 +1,66 @@
+/*
+ *  config_file_parser.h
+ *
+ *  Copyright 2014 Michael Zillgith
+ *
+ *  This file is part of libIEC61850.
+ *
+ *  libIEC61850 is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  libIEC61850 is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with libIEC61850.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef CONFIG_FILE_PARSER_H_
+#define CONFIG_FILE_PARSER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "hal_filesystem.h"
+
+/** \addtogroup server_api_group
+ *  @{
+ */
+
+/**
+ * @defgroup CONFIG_FILE_PARSER Create data models by configuration files
+ *
+ * \brief Functions to create data models from simple text configuration files create with the configuration file tool.
+ *
+ * @{
+ */
+
+/**
+ * \brief Create a data model from simple text configuration file
+ *
+ * \param filename name or path of the configuraton file
+ *
+ * \return the data model to be used by \ref IedServer
+ */
+LIB61850_API IedModel*
+ConfigFileParser_createModelFromConfigFileEx(const char* filename);
+
+LIB61850_API IedModel*
+ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle);
+
+/**@}*/
+
+/**@}*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CONFIG_FILE_PARSER_H_ */

+ 560 - 0
library/include/libiec61850/iec61850_dynamic_model.h

@@ -0,0 +1,560 @@
+/*
+ *  dynamic_model.h
+ *
+ *  Copyright 2014 Michael Zillgith
+ *
+ *  This file is part of libIEC61850.
+ *
+ *  libIEC61850 is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  libIEC61850 is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with libIEC61850.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef DYNAMIC_MODEL_H_
+#define DYNAMIC_MODEL_H_
+
+#include "iec61850_model.h"
+#include "iec61850_cdc.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/** \addtogroup server_api_group
+ *  @{
+ */
+
+/**
+ * @defgroup DYNAMIC_MODEL General dynamic model creation functions
+ *
+ * \brief Functions to dynamically create IEC 61850 data models without using SCL or configuration files
+ *
+ * @{
+ */
+
+
+/**
+ * \brief create a new IedModel instance
+ *
+ * The IedModel object is the root node of an IEC 61850 data model.
+ *
+ * \param name the name of the IedModel
+ *
+ * \return the new data model instance
+ */
+LIB61850_API IedModel*
+IedModel_create(const char* name);
+
+/**
+ * \brief Set the name of the IED (use only for dynamic model!)
+ *
+ * This will change the default name (usually "TEMPLATE") to a user configured values.
+ * NOTE: This function has to be called before IedServer_create !
+ * NOTE: For dynamic model (and configuration file date model) this function has to be
+ * used instead of IedModel_setIedName.
+ *
+ * \param model the IedModel instance
+ * \param the name of the configured IED
+ */
+LIB61850_API void
+IedModel_setIedNameForDynamicModel(IedModel* self, const char* name);
+
+/**
+ * \brief destroy a dynamically created data model
+ *
+ * This function will free all the memory allocated for the data model.
+ *
+ * NOTE: Do not use this function when using a static data model (static_model.c create by static model generator).
+ *
+ * \param model the model instance to destroy
+ */
+LIB61850_API void
+IedModel_destroy(IedModel* model);
+
+/**
+ * \brief Create a new logical device and add it to the IED model
+ *
+ * \param name the name of the new logical device
+ * \param parent the parent IED model
+ *
+ * \return the newly created LogicalDevice instance
+ */
+LIB61850_API LogicalDevice*
+LogicalDevice_create(const char* name, IedModel* parent);
+
+/**
+ * \brief Create a new logical device and add it to the IED model
+ *
+ * \param name the name of the new logical device
+ * \param parent the parent IED model
+ * \param ldName when not NULL functional naming is used for this LD (ldName <= 64 chars)
+ *
+ * \return the newly created LogicalDevice instance
+ */
+LIB61850_API LogicalDevice*
+LogicalDevice_createEx(const char* inst, IedModel* parent, const char* ldName);
+
+/**
+ * \brief Create a new logical mode  and add it to a logical device
+ *
+ * \param name the name of the new logical node
+ * \param parent the parent logical device
+ *
+ * \return the newly created LogicalNode instance
+ */
+LIB61850_API LogicalNode*
+LogicalNode_create(const char* name, LogicalDevice* parent);
+
+/**
+ * \brief create a new data object or array data object and add it to a parent model node
+ *
+ * The parent model node has to be of type DataObject or LogicalNode.
+ * 
+ * NOTE: When the data object is an array the array elements will also be created by this function.
+ * This will result in child DataObject instances for each array element.
+ *
+ * \param name the name of the data object (e.h. "Mod", "Health" ...)
+ * \param parent the parent model node
+ * \param arrayElements the number of array elements if the data object is an array or 0
+ *
+ * \return the newly create DataObject instance
+ */
+LIB61850_API DataObject*
+DataObject_create(const char* name, ModelNode* parent, int arrayElements);
+
+/**
+ * \brief create a new data attribute and add it to a parent model node
+ *
+ * The parent model node has to be of type DataObject or DataAttribute
+ *
+ * \param name the name of the data attribute (e.g. "stVal")
+ * \param parent the parent model node
+ * \param type the type of the data attribute (CONSTRUCTED if the type contains sub data attributes)
+ * \param fc the functional constraint (FC) of the data attribute
+ * \param triggerOptions the trigger options (dupd, dchg, qchg) that cause an event notification
+ * \param arrayElements the number of array elements if the data attribute is an array or 0
+ * \param sAddr an optional short address
+ *
+ * \return the newly create DataAttribute instance
+ */
+LIB61850_API DataAttribute*
+DataAttribute_create(const char* name, ModelNode* parent, DataAttributeType type, FunctionalConstraint fc,
+        uint8_t triggerOptions, int arrayElements, uint32_t sAddr);
+
+/**
+ * \brief Get the data type of the data attribute
+ *
+ * \param self the data attribute instance
+ *
+ * \return the data attribute type
+ */
+LIB61850_API DataAttributeType
+DataAttribute_getType(DataAttribute* self);
+
+/**
+ * \brief Get the functional constraint (FC) of the data attribute
+ *
+ * \param self the data attribute instance
+ *
+ * \return the functional constraint (FC) of the data attribute
+ */
+LIB61850_API FunctionalConstraint
+DataAttribute_getFC(DataAttribute* self);
+
+/**
+ * \brief Get the trigger options of the data attribute
+ *
+ * \param self the data attribute instance
+ *
+ * \return the trigger options (dupd, dchg, qchg) that cause an event notification
+ */
+LIB61850_API uint8_t
+DataAttribute_getTrgOps(DataAttribute* self);
+
+/**
+ * \brief Set the value of the data attribute (can be used to set default values before server is created)
+ * 
+ * \param self the data attribute instance
+ * \param value the new default value
+ */
+LIB61850_API void
+DataAttribute_setValue(DataAttribute* self, MmsValue* value);
+
+/**
+ * \brief create a new report control block (RCB)
+ *
+ * Create a new report control block (RCB) and add it to the given logical node (LN).
+ *
+ * \param name name of the RCB relative to the parent LN
+ * \param parent the parent LN.
+ * \param rptId of the report. If NULL the default report ID (object reference) is used.
+ * \param isBuffered true for a buffered RCB - false for unbuffered RCB
+ * \param dataSetName name (object reference) of the default data set or NULL if no data set
+ *        is set by default
+ * \param confRef the configuration revision
+ * \param trgOps the trigger options supported by this RCB (bit set)
+ * \param options the inclusion options. Specifies what elements are included in a report (bit set)
+ * \param bufTm the buffering time of the RCB in milliseconds (time between the first event and the preparation of the report).
+ * \param intgPd integrity period in milliseconds
+ *
+ * \return the new RCB instance.
+ */
+LIB61850_API ReportControlBlock*
+ReportControlBlock_create(const char* name, LogicalNode* parent, const char* rptId, bool isBuffered, const char*
+        dataSetName, uint32_t confRef, uint8_t trgOps, uint8_t options, uint32_t bufTm, uint32_t intgPd);
+
+/**
+ * \brief Set a pre-configured client for the RCB
+ *
+ * If set only the pre configured client should use this RCB instance
+ *
+ * \param self the RCB instance
+ * \param clientType the type of the client (0 = no client, 4 = IPv4 client, 6 = IPv6 client)
+ * \param clientAddress buffer containing the client address (4 byte in case of an IPv4 address, 16 byte in case of an IPv6 address, NULL for no client)
+ */
+LIB61850_API void
+ReportControlBlock_setPreconfiguredClient(ReportControlBlock* self, uint8_t clientType, const uint8_t* clientAddress);
+
+/**
+ * \brief Get the name of the RCB instance
+ *
+ * NOTE: the returned string is only valid during the lifetime of the ReportControlBlock instance!
+ *
+ * \param self the RCB instance
+ *
+ * \return the RCB instance name
+ */
+LIB61850_API const char*
+ReportControlBlock_getName(ReportControlBlock* self);
+
+/**
+ * \brief Is the RCB buffered or unbuffered?
+ *
+ * \param self the RCB instance
+ *
+ * \return true, in case of a buffered RCB, false otherwise
+ */
+LIB61850_API bool
+ReportControlBlock_isBuffered(ReportControlBlock* self);
+
+/**
+ * \brief Get the parent (LogicalNode) of the RCB instance
+ *
+ * \param self the RCB instance
+ *
+ * \return the parent (LogicalNode) of the RCB instance
+ */
+LIB61850_API LogicalNode*
+ReportControlBlock_getParent(ReportControlBlock* self);
+
+/**
+ * \brief Get the name of the currently set report ID
+ *
+ * \param self the RCB instance
+ *
+ * \return a null terminated string containing the current data set name (the string has to be released by the caller!)
+ */
+LIB61850_API char*
+ReportControlBlock_getRptID(ReportControlBlock* self);
+
+/**
+ * \brief Check if RCB instance is enabled
+ *
+ * \param self the RCB instance
+ *
+ * \return true when the RCB instance is enabled, false otherwise
+ */
+LIB61850_API bool
+ReportControlBlock_getRptEna(ReportControlBlock* self);
+
+/**
+ * \brief Get the name of the currenlty set data set
+ *
+ * \param self the RCB instance
+ *
+ * \return a null terminated string containing the current data set name (the string has to be released by the caller!)
+ */
+LIB61850_API char*
+ReportControlBlock_getDataSet(ReportControlBlock* self);
+
+/**
+ * \brief Get the confRev value
+ *
+ * \param self the RCB instance
+ *
+ * \return confRev value
+ */
+LIB61850_API uint32_t
+ReportControlBlock_getConfRev(ReportControlBlock* self);
+
+/**
+ * \brief Get the currently set OptFlds value
+ *
+ * The OptField (option field) value is a bit field with the following fields:
+ * - RPT_OPT_SEQ_NUM
+ * - RPT_OPT_TIME_STAMP
+ * - RPT_OPT_REASON_FOR_INCLUSION
+ * - RPT_OPT_DATA_SET
+ * - RPT_OPT_DATA_REFERENCE
+ * - RPT_OPT_BUFFER_OVERFLOW
+ * - RPT_OPT_ENTRY_ID
+ * - RPT_OPT_CONF_REV
+ *
+ * \param self the RCB instance
+ *
+ * \return OptFlds options value
+ */
+LIB61850_API uint32_t
+ReportControlBlock_getOptFlds(ReportControlBlock* self);
+
+/**
+ * \brief Get the BufTm value (buffer time)
+ *
+ * The buffer time is the maximum value between an event and
+ * the actual report generation.
+ *
+ * \param self the RCB instance
+ *
+ * \return bufTm value
+ */
+LIB61850_API uint32_t
+ReportControlBlock_getBufTm(ReportControlBlock* self);
+
+LIB61850_API uint16_t
+ReportControlBlock_getSqNum(ReportControlBlock* self);
+
+/**
+ * \brief Get the currently set trigger options
+ *
+ * The trigger option value is a bit field with the following fields:
+ * - TRG_OPT_DATA_CHANGED
+ * - TRG_OPT_QUALITY_CHANGED
+ * - TRG_OPT_DATA_UPDATE
+ * - TRG_OPT_INTEGRITY
+ * - TRG_OPT_GI
+ *
+ * \param self the RCB instance
+ *
+ * \return trigger options value
+ */
+LIB61850_API uint32_t
+ReportControlBlock_getTrgOps(ReportControlBlock* self);
+
+LIB61850_API uint32_t
+ReportControlBlock_getIntgPd(ReportControlBlock* self);
+
+LIB61850_API bool
+ReportControlBlock_getGI(ReportControlBlock* self);
+
+LIB61850_API bool
+ReportControlBlock_getPurgeBuf(ReportControlBlock* self);
+
+LIB61850_API MmsValue*
+ReportControlBlock_getEntryId(ReportControlBlock* self);
+
+LIB61850_API uint64_t
+ReportControlBlock_getTimeofEntry(ReportControlBlock* self);
+
+LIB61850_API int16_t
+ReportControlBlock_getResvTms(ReportControlBlock* self);
+
+LIB61850_API bool
+ReportControlBlock_getResv(ReportControlBlock* self);
+
+LIB61850_API MmsValue*
+ReportControlBlock_getOwner(ReportControlBlock* self);
+
+/**
+ * \brief create a new log control block (LCB)
+ *
+ * Create a new log control block (LCB) and add it to the given logical node (LN).
+ *
+ * \param name name of the LCB relative to the parent LN
+ * \param parent the parent LN.
+ * \param dataSetName name (object reference) of the default data set or NULL if no data set
+ *        is set by default
+ * \param logRef name (object reference) of the default log or NULL if no log is set by default. THe LDname doesn't contain the IED name!
+ * \param trgOps the trigger options supported by this LCB (bit set)
+ * \param intgPd integrity period in milliseconds
+ * \param logEna if true the log will be enabled by default, false otherwise
+ * \param reasonCode if true the reasonCode will be included in the log (this is always true in MMS mapping)
+ *
+ * \return the new LCB instance
+ */
+LIB61850_API LogControlBlock*
+LogControlBlock_create(const char* name, LogicalNode* parent, const char* dataSetName, const char* logRef, uint8_t trgOps,
+        uint32_t intgPd, bool logEna, bool reasonCode);
+
+LIB61850_API const char*
+LogControlBlock_getName(LogControlBlock* self);
+
+LIB61850_API LogicalNode*
+LogControlBlock_getParent(LogControlBlock* self);
+
+/**
+ * \brief create a log (used by the IEC 61850 log service)
+ *
+ * \param name name of the LOG relative to the parent LN
+ * \param parent the parent LN
+ *
+ * \return the new LOG instance
+ */
+LIB61850_API Log*
+Log_create(const char* name, LogicalNode* parent);
+
+/**
+ * \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
+ */
+LIB61850_API SettingGroupControlBlock*
+SettingGroupControlBlock_create(LogicalNode* parent, uint8_t actSG, uint8_t numOfSGs);
+
+/**
+ * \brief create a new GSE/GOOSE control block (GoCB)
+ *
+ * Create a new GOOSE control block (GoCB) and add it to the given logical node (LN)
+ *
+ * \param name name of the GoCB relative to the parent LN
+ * \param parent the parent LN
+ * \param appId the application ID of the GoCB
+ * \param dataSet the data set reference to be used by the GoCB
+ * \param confRev the configuration revision
+ * \param fixedOffs indicates if GOOSE publisher shall use fixed offsets (NOT YET SUPPORTED)
+ * \param minTime minimum GOOSE retransmission time (-1 if not specified - uses stack default then)
+ * \param maxTime GOOSE retransmission time in stable state (-1 if not specified - uses stack default then)
+ *
+ * \return the new GoCB instance
+ */
+LIB61850_API GSEControlBlock*
+GSEControlBlock_create(const char* name, LogicalNode* parent, const char* appId, const char* dataSet, uint32_t confRev,
+        bool fixedOffs, int minTime, int maxTime);
+
+/**
+ * \brief create a new Multicast/Unicast Sampled Value (SV) control block (SvCB)
+ *
+ * Create a new Sampled Value control block (SvCB) and add it to the given logical node (LN)
+ *
+ * \param name name of the SvCB relative to the parent LN
+ * \param parent the parent LN
+ * \param svID the application ID of the SvCB
+ * \param dataSet the data set reference to be used by the SVCB
+ * \param confRev the configuration revision
+ * \param smpMod the sampling mode used
+ * \param smpRate the sampling rate used
+ * \param optFlds the optional element configuration
+ *
+ * \return the new SvCB instance
+ */
+LIB61850_API SVControlBlock*
+SVControlBlock_create(const char* name, LogicalNode* parent, const char* svID, const char* dataSet, uint32_t confRev, uint8_t smpMod,
+        uint16_t smpRate, uint8_t optFlds, bool isUnicast);
+
+LIB61850_API void
+SVControlBlock_addPhyComAddress(SVControlBlock* self, PhyComAddress* phyComAddress);
+
+LIB61850_API void
+GSEControlBlock_addPhyComAddress(GSEControlBlock* self, PhyComAddress* phyComAddress);
+
+/**
+ * \brief create a PhyComAddress object
+ *
+ * A PhyComAddress object contains all required addressing information for a GOOSE publisher.
+ *
+ * \param vlanPriority the priority field of the VLAN tag
+ * \param vlanId the ID field of the VLAN tag
+ * \param appId the application identifier
+ * \param dstAddress the 6 byte multicast MAC address to specify the destination
+ *
+ * \return the new PhyComAddress object
+ */
+LIB61850_API PhyComAddress*
+PhyComAddress_create(uint8_t vlanPriority, uint16_t vlanId, uint16_t appId, uint8_t dstAddress[]);
+
+/**
+ * \brief create a new data set
+ *
+ * \param name the name of the data set
+ * \param parent the logical node that hosts the data set (typically a LLN0)
+ *
+ * \return the new data set instance
+ */
+LIB61850_API DataSet*
+DataSet_create(const char* name, LogicalNode* parent);
+
+/**
+ * \brief Get the name of the data set
+ *
+ * \param self the instance of the data set
+ *
+ * \returns the name of the data set (not the object reference).
+ */
+LIB61850_API const char*
+DataSet_getName(DataSet* self);
+
+/**
+ * \brief returns the number of elements (entries) of the data set
+ *
+ * \param self the instance of the data set
+ *
+ * \returns the number of data set elements
+ */
+LIB61850_API int
+DataSet_getSize(DataSet* self);
+
+LIB61850_API DataSetEntry*
+DataSet_getFirstEntry(DataSet* self);
+
+LIB61850_API DataSetEntry*
+DataSetEntry_getNext(DataSetEntry* self);
+
+/**
+ * \brief create a new data set entry (FCDA)
+ *
+ * Create a new FCDA reference and add it to the given data set as a new data set member.
+ *
+ * Note: Be aware that data set entries are not IEC 61850 object reference but MMS variable names
+ * that have to contain the LN name, the FC and subsequent path elements separated by "$" instead of ".".
+ * This is due to efficiency reasons to avoid the creation of additional strings.
+ *
+ * If the variable parameter does not contain a logical device name (separated from the remaining variable
+ * name by the "/" character) the logical device where the data set resides is used automatically.
+ *
+ * \param dataSet the data set to which the new entry will be added
+ * \param variable the name of the variable as MMS variable name  including FC ("$" used as separator!)
+ * \param index the index if the FCDA is an array element, otherwise -1
+ * \param component the name of the component of the variable if the FCDA is a sub element of an array
+ *        element. If this is not the case then NULL should be given here.
+ *
+ * \return the new data set entry instance
+ */
+LIB61850_API DataSetEntry*
+DataSetEntry_create(DataSet* dataSet, const char* variable, int index, const char* component);
+
+/**@}*/
+
+/**@}*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DYNAMIC_MODEL_H_ */

+ 632 - 0
library/include/libiec61850/iec61850_model.h

@@ -0,0 +1,632 @@
+/*
+ *  model.h
+ *
+ *  Copyright 2013-2024 Michael Zillgith
+ *
+ *  This file is part of libIEC61850.
+ *
+ *  libIEC61850 is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  libIEC61850 is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with libIEC61850.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef MODEL_H_
+#define MODEL_H_
+
+#include "iec61850_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \addtogroup server_api_group
+ *  @{
+ */
+
+/**
+ * @defgroup DATA_MODEL General data model definitions, access and iteration functions
+ *
+ * \brief Functions and structures to access and iterate the IEC 61850 data model
+ *
+ * @{
+ */
+
+/**
+ * \brief abstract base type for IEC 61850 data model nodes
+ */
+typedef struct sModelNode ModelNode;
+
+/**
+ * \brief IEC 61850 data model element of type data attribute
+ */
+typedef struct sDataAttribute DataAttribute;
+
+/**
+ * \brief IEC 61850 data model element of type data object
+ */
+typedef struct sDataObject DataObject;
+
+/**
+ * \brief IEC 61850 data model element of type logical node
+ */
+typedef struct sLogicalNode LogicalNode;
+
+/**
+ * \brief IEC 61850 data model element of type logical device
+ */
+typedef struct sLogicalDevice LogicalDevice;
+
+/**
+ * \brief Root node of the IEC 61850 data model. This is usually created by the model generator tool (genmodel.jar)
+ */
+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;
+
+typedef struct sSVControlBlock SVControlBlock;
+
+typedef struct sLogControlBlock LogControlBlock;
+
+typedef struct sLog Log;
+
+typedef enum {
+	IEC61850_UNKNOWN_TYPE = -1,
+	IEC61850_BOOLEAN = 0,/* int */
+	IEC61850_INT8 = 1,   /* int8_t */
+	IEC61850_INT16 = 2,  /* int16_t */
+	IEC61850_INT32 = 3,  /* int32_t */
+	IEC61850_INT64 = 4,  /* int64_t */
+	IEC61850_INT128 = 5, /* no native mapping! */
+	IEC61850_INT8U = 6,  /* uint8_t */
+	IEC61850_INT16U = 7, /* uint16_t */
+	IEC61850_INT24U = 8, /* uint32_t */
+	IEC61850_INT32U = 9, /* uint32_t */
+	IEC61850_FLOAT32 = 10, /* float */
+	IEC61850_FLOAT64 = 11, /* double */
+	IEC61850_ENUMERATED = 12,
+	IEC61850_OCTET_STRING_64 = 13,
+	IEC61850_OCTET_STRING_6 = 14,
+	IEC61850_OCTET_STRING_8 = 15,
+	IEC61850_VISIBLE_STRING_32 = 16,
+	IEC61850_VISIBLE_STRING_64 = 17,
+	IEC61850_VISIBLE_STRING_65 = 18,
+	IEC61850_VISIBLE_STRING_129 = 19,
+	IEC61850_VISIBLE_STRING_255 = 20,
+	IEC61850_UNICODE_STRING_255 = 21,
+	IEC61850_TIMESTAMP = 22,
+	IEC61850_QUALITY = 23,
+	IEC61850_CHECK = 24,
+	IEC61850_CODEDENUM = 25,
+	IEC61850_GENERIC_BITSTRING = 26,
+	IEC61850_CONSTRUCTED = 27,
+	IEC61850_ENTRY_TIME = 28,
+	IEC61850_PHYCOMADDR = 29,
+	IEC61850_CURRENCY = 30,
+	IEC61850_OPTFLDS = 31, /* bit-string(10) */
+	IEC61850_TRGOPS = 32 /* bit-string(6) */
+} DataAttributeType;
+
+typedef enum {
+    LogicalDeviceModelType,
+    LogicalNodeModelType,
+    DataObjectModelType,
+    DataAttributeModelType
+} ModelNodeType;
+
+struct sIedModel {
+    char* name;
+    LogicalDevice* firstChild;
+    DataSet* dataSets;
+    ReportControlBlock* rcbs;
+    GSEControlBlock* gseCBs;
+    SVControlBlock* svCBs;
+    SettingGroupControlBlock* sgcbs;
+    LogControlBlock* lcbs;
+    Log* logs;
+    void (*initializer) (void);
+};
+
+struct sLogicalDevice {
+    ModelNodeType modelType;
+    char* name; /* LD instance */
+    ModelNode* parent;
+    ModelNode* sibling;
+    ModelNode* firstChild;
+    char* ldName; /* ldName (when using functional naming) */
+};
+
+struct sModelNode {
+    ModelNodeType modelType;
+    char* name;
+    ModelNode* parent;
+    ModelNode* sibling;
+    ModelNode* firstChild;
+};
+
+struct sLogicalNode {
+    ModelNodeType modelType;
+    char* name;
+    ModelNode* parent;
+    ModelNode* sibling;
+    ModelNode* firstChild;
+};
+
+struct sDataObject {
+    ModelNodeType modelType;
+    char* name;
+    ModelNode* parent;
+    ModelNode* sibling;
+    ModelNode* firstChild;
+
+    int elementCount; /* value > 0 if this is an array */
+    int arrayIndex; /* value > -1 when this is an array element */
+};
+
+struct sDataAttribute {
+    ModelNodeType modelType;
+    char* name;
+    ModelNode* parent;
+    ModelNode* sibling;
+    ModelNode* firstChild;
+
+    int elementCount; /* value > 0 if this is an array */
+    int arrayIndex; /* value > -1 when this is an array element */
+
+    FunctionalConstraint fc;
+    DataAttributeType type;
+
+    uint8_t triggerOptions; /* TRG_OPT_DATA_CHANGED | TRG_OPT_QUALITY_CHANGED | TRG_OPT_DATA_UPDATE */
+
+    MmsValue* mmsValue;
+
+    uint32_t sAddr; /* TODO remove in version 2.0 */
+};
+
+typedef struct sDataSetEntry {
+    char* logicalDeviceName; /* logical device instance name */
+    bool isLDNameDynamicallyAllocated;
+    char* variableName;
+    int index;
+    char* componentName;
+    MmsValue* value;
+    struct sDataSetEntry* sibling;
+} DataSetEntry;
+
+struct sDataSet {
+    char* logicalDeviceName; /* logical device instance name */
+    char* name; /* eg. MMXU1$dataset1 */
+    int elementCount;
+    DataSetEntry* fcdas;
+    DataSet* sibling;
+};
+
+struct sReportControlBlock {
+    LogicalNode* parent;
+    char* name;
+    char* rptId;
+    bool buffered;
+    char* dataSetName; /* pre loaded with relative name in logical node */
+
+    uint32_t confRef; /* ConfRef - configuration revision */
+    uint8_t trgOps; /* TrgOps - trigger conditions */
+    uint8_t options; /* OptFlds */
+    uint32_t bufferTime; /* BufTm - time to buffer events until a report is generated */
+    uint32_t intPeriod; /* IntgPd - integrity period */
+
+    /* type (first byte) and address of the pre-configured client
+       type can be one of (0 - no reservation, 4 - IPv4 client, 6 - IPv6 client) */
+    uint8_t clientReservation[17];
+
+    ReportControlBlock* sibling; /* next control block in list or NULL if this is the last entry
+                                  * at runtime reuse as pointer to ReportControl instance!
+                                  **/
+};
+
+struct sLogControlBlock {
+    LogicalNode* parent;
+
+    char* name;
+
+    char* dataSetName;
+    char* logRef;        /* object reference to the journal */
+
+    uint8_t trgOps;      /* TrgOps - trigger conditions */
+    uint32_t intPeriod;  /* IntgPd - integrity period */
+
+    bool logEna;         /* enable log by default */
+    bool reasonCode;     /* include reason code in log */
+
+    LogControlBlock* sibling; /* next control block in list or NULL if this is the last entry */
+};
+
+struct sLog {
+    LogicalNode* parent;
+
+    char* name;
+
+    Log* sibling; /* next log instance 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 */
+};
+
+struct sGSEControlBlock {
+    LogicalNode* parent;
+    char* name;
+    char* appId;
+    char* dataSetName; /* pre loaded with relative name in logical node */
+    uint32_t confRev;  /* ConfRev - configuration revision */
+    bool fixedOffs;    /* fixed offsets */
+    PhyComAddress* address; /* GSE communication parameters */
+    int minTime; /* optional minTime parameter --> -1 if not present */
+    int maxTime; /* optional maxTime parameter --> -1 if not present */
+    GSEControlBlock* sibling; /* next control block in list or NULL if this is the last entry */
+};
+
+struct sSVControlBlock {
+    LogicalNode* parent;
+    char* name;
+
+    char* svId; /* MsvUD/UsvID */
+    char* dataSetName; /* pre loaded with relative name in logical node */
+
+    uint8_t optFlds;
+
+    uint8_t smpMod;
+    uint16_t smpRate;
+
+    uint32_t confRev;  /* ConfRev - configuration revision */
+
+    PhyComAddress* dstAddress; /* SV communication parameters */
+
+    bool isUnicast;
+
+    int noASDU;
+
+    SVControlBlock* sibling; /* next control block in list or NULL if this is the last entry */
+};
+
+/**
+ * \brief get the number of direct children of a model node
+ *
+ * \param self the model node instance
+ *
+ * \return the number of children of the model node
+ * ¸
+ */
+LIB61850_API int
+ModelNode_getChildCount(ModelNode* self);
+
+/**
+ * \brief return a child model node
+ *
+ * \param self the model node instance
+ * \param name the name of the child model node
+ *
+ * \return the model node instance or NULL if model node does not exist.
+ */
+LIB61850_API ModelNode*
+ModelNode_getChild(ModelNode* self, const char* name);
+
+/**
+ * \brief return the child node of an array or other structure
+ *
+ * \param self the model node instance
+ * \param idx the index (e.g. array index) starting with 0
+ *
+ * \return the model node instance or NULL if model node with given index does not exist.
+ */
+LIB61850_API ModelNode*
+ModelNode_getChildWithIdx(ModelNode* self, int idx);
+
+/**
+ * \brief return a child model node with a given functional constraint
+ *
+ * Sometimes the name is not enough to identify a model node. This is the case when
+ * editable setting groups are used. In this case the setting group members have two different
+ * model nodes associated that differ in their FC (SG and SE).
+ *
+ * \param self the model node instance
+ * \param name the name of the child model node
+ * \param fc the functional constraint of the model node
+ *
+ * \return  the model node instance or NULL if model node does not exist.
+ */
+LIB61850_API ModelNode*
+ModelNode_getChildWithFc(ModelNode* self, const char* name, FunctionalConstraint fc);
+
+/**
+ * \brief Return the IEC 61850 object reference of a model node
+ *
+ * \param self the model node instance
+ * \param objectReference pointer to a buffer where to write the object reference string. If NULL
+ *        is given the buffer is allocated by the function.
+ *
+ * \return the object reference string
+ */
+LIB61850_API char*
+ModelNode_getObjectReference(ModelNode* self, char* objectReference);
+
+/**
+ * \brief Return the IEC 61850 object reference of a model node
+ *
+ * \param self the model node instance
+ * \param objectReference pointer to a buffer where to write the object reference string. If NULL
+ *        is given the buffer is allocated by the function.
+ * \param withoutIedName create object reference without IED name part
+ *
+ * \return the object reference string
+ */
+LIB61850_API char*
+ModelNode_getObjectReferenceEx(ModelNode* node, char* objectReference, bool withoutIedName);
+
+/**
+ * \brief Get the type of the ModelNode
+ *
+ * \param self the ModelNode instance
+ *
+ * \return the type of the ModelNode (one of LD, LN, DO, DA)
+ */
+LIB61850_API ModelNodeType
+ModelNode_getType(ModelNode* self);
+
+/**
+ * \brief Get the name of the ModelNode
+ *
+ * \param self the ModelNode instance
+ *
+ * \return the name of the ModelNode
+ */
+LIB61850_API const char*
+ModelNode_getName(ModelNode* self);
+
+/**
+ * \brief Get the parent ModelNode of this ModelNode instance
+ *
+ * \param self the ModelNode instance
+ *
+ * \return the parent instance, or NULL when the ModelNode has no parent
+ */
+LIB61850_API ModelNode*
+ModelNode_getParent(ModelNode* self);
+
+/**
+ * \brief Get the list of direct child nodes
+ *
+ * \param self the ModelNode instance
+ *
+ * \return the list of private child nodes, or NULL when the node has no children
+ */
+LIB61850_API LinkedList
+ModelNode_getChildren(ModelNode* self);
+
+/**
+ * \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
+ */
+LIB61850_API void
+IedModel_setIedName(IedModel* self, const char* iedName);
+
+/**
+ * \brief Lookup a model node by its object reference
+ *
+ * This function uses the full logical device name as part of the object reference
+ * as it happens to appear on the wire. E.g. if IED name in SCL file would be "IED1"
+ * and the logical device "WD1" the resulting LD name would be "IED1WD".
+ * When using functional naming in the LD (with ldName attribute) then the logical
+ * device name is identical with the ldName attribute.
+ *
+ * \param self the IedModel instance that holds the model node
+ * \param objectReference the IEC 61850 object reference
+ *
+ * \return the model node instance or NULL if model node does not exist.
+ */
+LIB61850_API ModelNode*
+IedModel_getModelNodeByObjectReference(IedModel* self, const char* objectReference);
+
+LIB61850_API SVControlBlock*
+IedModel_getSVControlBlock(IedModel* self, LogicalNode* parentLN, const char* svcbName);
+
+/**
+ * \brief Lookup a model node by its short (normalized) reference
+ *
+ * This version uses the object reference that does not contain the
+ * IED name or functional name as part of the logical device name.
+ * Instead the LD part consists of the LD instance name ("inst" attribute).
+ * This function is useful for
+ * devices where the IED name can be configured.
+ *
+ * \param self the IedModel instance that holds the model node
+ * \param objectReference the IEC 61850 object reference
+ *
+ * \return the model node instance or NULL if model node does not exist.
+ */
+LIB61850_API ModelNode*
+IedModel_getModelNodeByShortObjectReference(IedModel* self, const char* objectReference);
+
+/**
+ * \brief Lookup a model node by its short address
+ *
+ * Short address is a 32 bit unsigned integer as specified in the "sAddr" attribute of
+ * the ICD file or in the configuration file.
+ *
+ * \param self the IedModel instance that holds the model node
+ * \param shortAddress
+ *
+ * \return the model node instance or NULL if model node does not exist.
+ */
+LIB61850_API ModelNode*
+IedModel_getModelNodeByShortAddress(IedModel* self, uint32_t shortAddress);
+
+/**
+ * \brief Lookup logical device (LD) by device instance name (SCL attribute "inst")
+ *
+ * \param self IedModel instance
+ * \param ldInst the logical device instance name (SCL attribute "inst")
+ *
+ * \return The matching LogicalDevice instance
+ */
+LIB61850_API LogicalDevice*
+IedModel_getDeviceByInst(IedModel* self, const char* ldInst);
+
+/**
+ * \brief Lookup logical device (LD) instance by index
+ *
+ * \param self IedModel instance
+ * \param index the index of the LD in the range (0 .. number of LDs - 1)
+ *
+ * \return the corresponding LogicalDevice* object or NULL if the index is out of range
+ */
+LIB61850_API LogicalDevice*
+IedModel_getDeviceByIndex(IedModel* self, int index);
+
+
+/**
+ * \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
+ */
+LIB61850_API LogicalNode*
+LogicalDevice_getLogicalNode(LogicalDevice* self, 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
+ */
+LIB61850_API SettingGroupControlBlock*
+LogicalDevice_getSettingGroupControlBlock(LogicalDevice* self);
+
+/**@}*/
+
+/**@}*/
+
+
+/**
+ * \brief unset all MmsValue references in the data model
+ *
+ * \param self the IedModel instance that holds the model node
+ */
+LIB61850_API void
+IedModel_setAttributeValuesToNull(IedModel* self);
+
+/**
+ * \brief Lookup logical device (LD) by device name
+ *
+ * \param self IedModel instance
+ * \param ldInst the logical device name (as it is seen from the protocol side - MMS domain name)
+ *
+ * \return The matching LogicalDevice instance
+ */
+LIB61850_API LogicalDevice*
+IedModel_getDevice(IedModel* self, const char* ldName);
+
+/**
+ * \brief Lookup a data set in the IED model
+ *
+ * \param self IedModel instance
+ * \param dataSetReference MMS mapping object reference! e.g. ied1Inverter/LLN0$dataset1
+ *
+ * \return The matching DataSet instance
+ */
+LIB61850_API DataSet*
+IedModel_lookupDataSet(IedModel* self, const char* dataSetReference);
+
+/**
+ * \brief Lookup a DataAttribute instance with the corresponding MmsValue instance
+ *
+ * \param self IedModel instance
+ * \param value the MmsValue instance (from the MMS value cache)
+ *
+ * \return the matching DataAttribute instance
+ */
+LIB61850_API DataAttribute*
+IedModel_lookupDataAttributeByMmsValue(IedModel* self, MmsValue* value);
+
+
+/**
+ * \brief Get the number of logical devices
+ *
+ * \param self IedModel instance
+ *
+ * \return the number of logical devices
+ */
+LIB61850_API int
+IedModel_getLogicalDeviceCount(IedModel* self);
+
+LIB61850_API int
+LogicalDevice_getLogicalNodeCount(LogicalDevice* self);
+
+LIB61850_API ModelNode*
+LogicalDevice_getChildByMmsVariableName(LogicalDevice* self, const char* mmsVariableName);
+
+LIB61850_API bool
+LogicalNode_hasFCData(LogicalNode* self, FunctionalConstraint fc);
+
+LIB61850_API bool
+LogicalNode_hasBufferedReports(LogicalNode* self);
+
+LIB61850_API bool
+LogicalNode_hasUnbufferedReports(LogicalNode* self);
+
+/**
+ * \brief get a data set instance
+ *
+ * \param self the logical node instance of the data set
+ * \param dataSetName the name of the data set
+ *
+ * \return the data set instance or NULL if the data set does not exist
+ */
+LIB61850_API DataSet*
+LogicalNode_getDataSet(LogicalNode* self, const char* dataSetName);
+
+LIB61850_API bool
+DataObject_hasFCData(DataObject* self, FunctionalConstraint fc);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* MODEL_H_ */

+ 2089 - 0
library/include/libiec61850/iec61850_server.h

@@ -0,0 +1,2089 @@
+/*
+ *  iec61850_server.h
+ *
+ *  IEC 61850 server API for libiec61850.
+ *
+ *  Copyright 2013-2024 Michael Zillgith
+ *
+ *  This file is part of libIEC61850.
+ *
+ *  libIEC61850 is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  libIEC61850 is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with libIEC61850.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ *
+ */
+
+#ifndef IED_SERVER_API_H_
+#define IED_SERVER_API_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \defgroup server_api_group IEC 61850/MMS server API
+ *
+ * \brief IEC 61850/MMS server API for libiec61850.
+ *
+ * This API can be used to create, configure, and run IEC 61850/MMS server instances.
+ *
+ *  @{
+ */
+
+#include "mms_server.h"
+#include "iec61850_dynamic_model.h"
+#include "iec61850_model.h"
+#include "hal_filesystem.h"
+#include "iso_connection_parameters.h"
+#include "iec61850_config_file_parser.h"
+
+/**
+ * @defgroup IEC61850_SERVER_CONFIG Server configuration related functions
+ *
+ * \brief Functions to handle server independent configuration settings to define
+ *        services, features, and other behavior of IEC 61850 server instances.
+ *
+ * @{
+ */
+
+#define IEC61850_REPORTSETTINGS_RPT_ID 1
+#define IEC61850_REPORTSETTINGS_BUF_TIME 2
+#define IEC61850_REPORTSETTINGS_DATSET 4
+#define IEC61850_REPORTSETTINGS_TRG_OPS 8
+#define IEC61850_REPORTSETTINGS_OPT_FIELDS 16
+#define IEC61850_REPORTSETTINGS_INTG_PD 32
+
+/**
+ * \brief Configuration object to configure IEC 61850 stack features
+ */
+typedef struct sIedServerConfig* IedServerConfig;
+
+struct sIedServerConfig
+{
+    /** size of the report buffer associated with a buffered report control block */
+    int reportBufferSize;
+
+    /** size of the report buffer associated with an unbuffered report control block */
+    int reportBufferSizeURCBs;
+
+    /** Base path (directory where the file service serves files */
+    char* fileServiceBasepath;
+
+    /** when true (default) enable MMS file service */
+    bool enableFileService;
+
+    /** when true (default) enable dynamic data set services for MMS */
+    bool enableDynamicDataSetService;
+
+    /** the maximum number of allowed association specific data sets */
+    int maxAssociationSpecificDataSets;
+
+    /** the maximum number of allowed domain specific data sets */
+    int maxDomainSpecificDataSets;
+
+    /** maximum number of data set entries of dynamic data sets */
+    int maxDataSetEntries;
+
+    /** when true (default) enable log service */
+    bool enableLogService;
+
+    /** when true (default) the integrated GOOSE publisher is used */
+    bool useIntegratedGoosePublisher;
+
+    /** IEC 61850 edition (0 = edition 1, 1 = edition 2, 2 = edition 2.1, ...) */
+    uint8_t edition;
+
+    /** maximum number of MMS (TCP) connections */
+    int maxMmsConnections;
+
+    /** enable EditSG service (default: true) */
+    bool enableEditSG;
+
+    /** enable visibility of SGCB.ResvTms (default: true) */
+    bool enableResvTmsForSGCB;
+
+    /** BRCB has resvTms attribute - only edition 2 (default: true) */
+    bool enableResvTmsForBRCB;
+
+    /** RCB has owner attribute (default: true) */
+    bool enableOwnerForRCB;
+
+    /** integrity report start times will by synchronized with straight numbers (default: false) */
+    bool syncIntegrityReportTimes;
+
+    /** for each configurable ReportSetting there is a separate flag (default: Dyn = enable write for all) */
+    uint8_t reportSettingsWritable;
+};
+
+/**
+ * \brief Create a new configuration object
+ *
+ * \return a new configuration object with default configuration values
+ */
+LIB61850_API IedServerConfig
+IedServerConfig_create(void);
+
+/**
+ * \brief Destroy the configuration object
+ */
+LIB61850_API void
+IedServerConfig_destroy(IedServerConfig self);
+
+/**
+ * \brief Set the IEC 61850 standard edition to use (default is edition 2)
+ *
+ * \param edition IEC_61850_EDITION_1, IEC_61850_EDITION_2, or IEC_61850_EDITION_2_1
+ */
+LIB61850_API void
+IedServerConfig_setEdition(IedServerConfig self, uint8_t edition);
+
+/**
+ * \brief Get the configued IEC 61850 standard edition
+ *
+ * \returns IEC_61850_EDITION_1, IEC_61850_EDITION_2, or IEC_61850_EDITION_2_1
+ */
+LIB61850_API uint8_t
+IedServerConfig_getEdition(IedServerConfig self);
+
+/**
+ * \brief Set the report buffer size for buffered reporting
+ *
+ * \param reportBufferSize the buffer size for each buffered report control block
+ */
+LIB61850_API void
+IedServerConfig_setReportBufferSize(IedServerConfig self, int reportBufferSize);
+
+/**
+ * \brief Gets the report buffer size for buffered reporting
+ *
+ * \return the buffer size for each buffered report control block
+ */
+LIB61850_API int
+IedServerConfig_getReportBufferSize(IedServerConfig self);
+
+/**
+ * \brief Set the report buffer size for unbuffered reporting
+ *
+ * \param reportBufferSize the buffer size for each unbuffered report control block
+ */
+LIB61850_API void
+IedServerConfig_setReportBufferSizeForURCBs(IedServerConfig self, int reportBufferSize);
+
+/**
+ * \brief Gets the report buffer size for unbuffered reporting
+ *
+ * \return the buffer size for each unbuffered report control block
+ */
+LIB61850_API int
+IedServerConfig_getReportBufferSizeForURCBs(IedServerConfig self);
+
+/**
+ * \brief Set the maximum number of MMS (TCP) connections the server accepts
+ *
+ * NOTE: Parameter has to be smaller than CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS if
+ * CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS != -1
+ *
+ * \param maxConnection maximum number of TCP connections
+ */
+LIB61850_API void
+IedServerConfig_setMaxMmsConnections(IedServerConfig self, int maxConnections);
+
+/**
+ * \brief Get the maximum number of MMS (TCP) connections the server accepts
+ *
+ * \return maximum number of TCP connections
+ */
+LIB61850_API int
+IedServerConfig_getMaxMmsConnections(IedServerConfig self);
+
+/**
+ * \brief Enable synchronized integrity report times
+ *
+ * NOTE: When this flag is enabled the integrity report generation times are
+ * aligned with the UTC epoch. Then the unix time stamps are straight multiples of the
+ * integrity interval.
+ *
+ * \param enable when true synchronized integrity report times are enabled
+ */
+LIB61850_API void
+IedServerConfig_setSyncIntegrityReportTimes(IedServerConfig self, bool enable);
+
+/**
+ * \brief Check if synchronized integrity report times are enabled
+ *
+ * NOTE: When this flag is enabled the integrity report generation times are
+ * aligned with the UTC epoch. Then the unix time stamps are straight multiples of the
+ * integrity interval.
+ *
+ * \return true, when enabled, false otherwise
+ */
+LIB61850_API bool
+IedServerConfig_getSyncIntegrityReportTimes(IedServerConfig self);
+
+/**
+ * \brief Set the basepath of the file services
+ *
+ * NOTE: the basepath specifies the local directory that is served by MMS file services
+ *
+ * \param basepath new file service base path
+ */
+LIB61850_API void
+IedServerConfig_setFileServiceBasePath(IedServerConfig self, const char* basepath);
+
+/**
+ * \brief Get the basepath of the file services
+ */
+LIB61850_API const char*
+IedServerConfig_getFileServiceBasePath(IedServerConfig self);
+
+/**
+ * \brief Enable/disable the MMS file service support
+ *
+ * \param[in] enable set true to enable the file services, otherwise false
+ */
+LIB61850_API void
+IedServerConfig_enableFileService(IedServerConfig self, bool enable);
+
+/**
+ * \brief Is the MMS file service enabled or disabled
+ *
+ * \return true if enabled, false otherwise
+ */
+LIB61850_API bool
+IedServerConfig_isFileServiceEnabled(IedServerConfig self);
+
+/**
+ * \brief Enable/disable the dynamic data set service for MMS
+ *
+ * \param[in] enable set true to enable dynamic data set service, otherwise false
+ */
+LIB61850_API void
+IedServerConfig_enableDynamicDataSetService(IedServerConfig self, bool enable);
+
+/**
+ * \brief Is the dynamic data set service for MMS enabled or disabled
+ *
+ * \return true if enabled, false otherwise
+ */
+LIB61850_API bool
+IedServerConfig_isDynamicDataSetServiceEnabled(IedServerConfig self);
+
+/**
+ * \brief Set the maximum allowed number of association specific (non-permanent) data sets
+ *
+ * NOTE: This specifies the maximum number of non-permanent data sets per connection. When
+ * the connection is closed these data sets are deleted automatically.
+ *
+ * \param maxDataSets maximum number of allowed data sets.
+ */
+LIB61850_API void
+IedServerConfig_setMaxAssociationSpecificDataSets(IedServerConfig self, int maxDataSets);
+
+/**
+ * \brief Get the maximum allowed number of association specific (non-permanent) data sets
+ *
+ * \return maximum number of allowed data sets.
+ */
+LIB61850_API int
+IedServerConfig_getMaxAssociationSpecificDataSets(IedServerConfig self);
+
+/**
+ * \brief Set the maximum allowed number of domain specific (permanent) data sets
+ *
+ * \param maxDataSets maximum number of allowed data sets.
+ */
+LIB61850_API void
+IedServerConfig_setMaxDomainSpecificDataSets(IedServerConfig self, int maxDataSets);
+
+/**
+ * \brief Get the maximum allowed number of domain specific (permanent) data sets
+ *
+ * \return maximum number of allowed data sets.
+ */
+LIB61850_API int
+IedServerConfig_getMaxDomainSpecificDataSets(IedServerConfig self);
+
+/**
+ * \brief Set the maximum number of entries in dynamic data sets
+ *
+ * NOTE: this comprises the base data set entries (can be simple or complex variables).
+ * When the client tries to create a data set with more member the request will be
+ * rejected and the data set will not be created.
+ *
+ * \param maxDataSetEntries the maximum number of entries allowed in a data set
+ */
+LIB61850_API void
+IedServerConfig_setMaxDataSetEntries(IedServerConfig self, int maxDataSetEntries);
+
+/**
+ * \brief Get the maximum number of entries in dynamic data sets
+ *
+ * \return the maximum number of entries allowed in a data sets
+ */
+LIB61850_API int
+IedServerConfig_getMaxDatasSetEntries(IedServerConfig self);
+
+/**
+ * \brief Enable/disable the log service for MMS
+ *
+ * \param[in] enable set true to enable log service, otherwise false
+ */
+LIB61850_API void
+IedServerConfig_enableLogService(IedServerConfig self, bool enable);
+
+/**
+ * \brief Enable/disable the EditSG service to allow clients to change setting groups (default is enabled)
+ *
+ * NOTE: When disabled SGCB.ResvTms is not available online and the setting of \ref IedServerConfig_enableResvTmsForSGCB
+ * is ignored.
+ *
+ * \param[in] enable set true to enable, otherwise false (default value it true)
+ */
+LIB61850_API void
+IedServerConfig_enableEditSG(IedServerConfig self, bool enable);
+
+/**
+ * \brief Enable/disable the SGCB.ResvTms when EditSG is enabled
+ *
+ * NOTE: When EditSG is disabled (see \ref IedServerConfig_enableEditSG) then this setting is ignored.
+ *
+ * \param[in] enable set true to enable, otherwise false (default value it true)
+ */
+LIB61850_API void
+IedServerConfig_enableResvTmsForSGCB(IedServerConfig self, bool enable);
+
+/**
+ * \brief Enable/disable the presence of BRCB.ResvTms (default value is true)
+ *
+ * \param[in] enable set true to enable, otherwise false
+ */
+LIB61850_API void
+IedServerConfig_enableResvTmsForBRCB(IedServerConfig self, bool enable);
+
+/**
+ * \brief ResvTms for BRCB enabled (visible)
+ *
+ * \return true if enabled, false otherwise
+ */
+LIB61850_API  bool
+IedServerConfig_isResvTmsForBRCBEnabled(IedServerConfig self);
+
+/**
+ * \brief Enable/disable the presence of owner in report control blocks (default value is false);
+ *
+ * \param[in] enable set true to enable, otherwise false
+ */
+LIB61850_API void
+IedServerConfig_enableOwnerForRCB(IedServerConfig self, bool enable);
+
+/**
+ * \brief Owner for RCBs enabled (visible)
+ *
+ * \return true if enabled, false otherwise
+ */
+LIB61850_API  bool
+IedServerConfig_isOwnerForRCBEnabled(IedServerConfig self);
+
+/**
+ * \brief Enable/disable using the integrated GOOSE publisher for configured GoCBs
+ *
+ * This is enabled by default. Disable it when you want to use a separate GOOSE publisher
+ *
+ * \param[in] enable set true to enable the integrated GOOSE publisher, otherwise false
+ */
+LIB61850_API void
+IedServerConfig_useIntegratedGoosePublisher(IedServerConfig self, bool enable);
+
+/**
+ * \brief Is the log service for MMS enabled or disabled
+ *
+ * \return true if enabled, false otherwise
+ */
+LIB61850_API bool
+IedServerConfig_isLogServiceEnabled(IedServerConfig self);
+
+/**
+ * \brief Make a configurable report setting writeable or read-only
+ *
+ * \note Can be used to implement some of Services\ReportSettings options
+ *
+ * \param[in] setting one of IEC61850_REPORTSETTINGS_RPT_ID, _BUF_TIME, _DATSET, _TRG_OPS, _OPT_FIELDS, _INTG_PD
+ * \param[in] isDyn true, when setting is writable ("Dyn") or false, when read-only
+ */
+LIB61850_API void
+IedServerConfig_setReportSetting(IedServerConfig self, uint8_t setting, bool isDyn);
+
+/**
+ * \brief Check if a configurable report setting is writable or read-only
+ *
+ * \param[in] setting one of IEC61850_REPORTSETTINGS_RPT_ID, _BUF_TIME, _DATSET, _TRG_OPS, _OPT_FIELDS, _INTG_PD
+ *
+ * \return  true, when setting is writable ("Dyn") or false, when read-only
+ */
+LIB61850_API bool
+IedServerConfig_getReportSetting(IedServerConfig self, uint8_t setting);
+
+/**@}*/
+
+/**
+ * @defgroup IEC61850_SERVER_GENERAL General server setup and management functions
+ *
+ * \brief Functions to create, configure, and manage an IEC 61850 server instance.
+ *
+ * @{
+ */
+
+/**
+ * An opaque handle for an IED server instance
+ */
+typedef struct sIedServer* IedServer;
+
+/**
+ * An opaque handle for a client connection
+ */
+typedef struct sClientConnection* ClientConnection;
+
+/**
+ * \brief Create a new IedServer instance
+ *
+ * \param dataModel reference to the IedModel data structure to be used as IEC 61850 data model of the device
+ *
+ * \return the new IedServer instance
+ */
+LIB61850_API IedServer
+IedServer_create(IedModel* dataModel);
+
+/**
+ * \brief Create a new IedServer with TLS support
+ *
+ * \param dataModel reference to the IedModel data structure to be used as IEC 61850 data model of the device
+ * \param tlsConfiguration TLS configuration object, or NULL to not use TLS
+ *
+ * \return the new IedServer instance
+ */
+LIB61850_API IedServer
+IedServer_createWithTlsSupport(IedModel* dataModel, TLSConfiguration tlsConfiguration);
+
+/**
+ * \brief Create new new IedServer with extended configurations parameters
+ *
+ * \param dataModel reference to the IedModel data structure to be used as IEC 61850 data model of the device
+ * \param tlsConfiguration TLS configuration object, or NULL to not use TLS
+ * \param serverConfiguration IED server configuration object for advanced server configuration
+ */
+LIB61850_API IedServer
+IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguration, IedServerConfig serverConfiguration);
+
+/**
+ * \brief Destroy an IedServer instance and release all resources (memory, TCP sockets)
+ *
+ * \param self the instance of IedServer to operate on.
+ */
+LIB61850_API void
+IedServer_destroy(IedServer self);
+
+/**
+ * \brief Add a new local access point (server will listen to provided IP/port combination)
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param ipAddr the local IP address to listen on
+ * \param tcpPort the local TCP port to listen on or -1 to use the standard TCP port
+ * \oaram tlsConfiguration the TLS configuration or NULL when TLS is not used for this access point
+ *
+ * \return true in case of success, false otherwise
+ */
+LIB61850_API bool
+IedServer_addAccessPoint(IedServer self, const char* ipAddr, int tcpPort, TLSConfiguration tlsConfiguration);
+
+/**
+ * \brief Set the local IP address to listen on
+ *
+ * \param self the IedServer instance
+ * \param localIpAddress the local IP address as C string (an internal copy will be created)
+ */
+LIB61850_API void
+IedServer_setLocalIpAddress(IedServer self, const char* localIpAddress);
+
+/**
+ * \brief Set the identify for the MMS identify service
+ *
+ * CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY required
+ *
+ * \param self the IedServer instance
+ * \param vendor the IED vendor name
+ * \param model the IED model name
+ * \param revision the IED revision/version number
+ */
+LIB61850_API void
+IedServer_setServerIdentity(IedServer self, const char* vendor, const char* model, const char* revision);
+
+/**
+ * \brief Set the virtual filestore basepath for the MMS file services
+ *
+ * All external file service accesses will be mapped to paths relative to the base directory.
+ * NOTE: This function is only available when the CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME
+ * option in stack_config.h is set.
+ *
+ * \param self the IedServer instance
+ * \param basepath the new virtual filestore basepath
+ */
+LIB61850_API void
+IedServer_setFilestoreBasepath(IedServer self, const char* basepath);
+
+/**
+ * \brief Assign a \ref LogStorage instance to a log reference
+ *
+ * \note configuration option CONFIG_IEC61850_LOG_SERVICE is required
+ *
+ * \param self the IedServer instance
+ * \param logRef the log reference to assign the log storage to
+ * \param logStorage the log storage instance to assign
+ */
+LIB61850_API void
+IedServer_setLogStorage(IedServer self, const char* logRef, LogStorage logStorage);
+
+/**
+ * \brief Start handling client connections
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param tcpPort the TCP port the server is listening (-1 for using the default MMS or secure MMS port)
+ */
+LIB61850_API void
+IedServer_start(IedServer self, int tcpPort);
+
+/**
+ * \brief Stop handling client connections
+ *
+ * \param self the instance of IedServer to operate on.
+ */
+LIB61850_API 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 (-1 for using the default MMS or secure MMS port)
+ */
+LIB61850_API void
+IedServer_startThreadless(IedServer self, int tcpPort);
+
+/**
+ * \brief Wait until a server connection is ready (with timeout)
+ *
+ * The function will wait until a server connection is ready (data available) or the
+ * timeout elapsed. This function is intended for the non-threaded mode. Its use is optional.
+ * It is equivalent of using select on sockets on Linux. If the return value is != 0 the
+ * IedServer_processIncomingData function should be called.
+ *
+ * \param self the IedServer instance
+ * \param timeoutMs the timeout to wait when no connection is ready
+ *
+ * \return 0 if no connection is ready; otherwise at least one connection is ready
+ */
+LIB61850_API 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.
+ */
+LIB61850_API 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.
+ */
+LIB61850_API void
+IedServer_performPeriodicTasks(IedServer self);
+
+/**
+ * \brief Stop handling client connections for non-threaded mode
+ *
+ * \param self the instance of IedServer to operate on.
+ */
+LIB61850_API void
+IedServer_stopThreadless(IedServer self);
+
+/**
+ * \brief Return the data model of the server
+ *
+ * \param self the instance of IedServer to operate on.
+ *
+ * \return the IedModel* instance of the server
+ */
+LIB61850_API IedModel*
+IedServer_getDataModel(IedServer self);
+
+/**
+ * \brief Check if IedServer instance is listening for client connections
+ *
+ * \param self the instance of IedServer to operate on.
+ *
+ * \return true if IedServer instance is listening for client connections
+ */
+LIB61850_API bool
+IedServer_isRunning(IedServer self);
+
+/**
+ * \brief Get number of open MMS connections
+ *
+ * \param self the instance of IedServer to operate on
+ *
+ * \return the number of open and accepted MMS connections
+ */
+LIB61850_API int
+IedServer_getNumberOfOpenConnections(IedServer self);
+
+/**
+ * \brief Get access to the underlying MmsServer instance.
+ *
+ * This function should be handled with care. Since direct interaction with the
+ * MmsServer can interfere with the IedServer.
+ *
+ * \param self the instance of IedServer to operate on.
+ *
+ * \return MmsServer instance that is used by the IedServer
+ */
+LIB61850_API MmsServer
+IedServer_getMmsServer(IedServer self);
+
+/**
+ * \brief Enable all GOOSE control blocks.
+ *
+ * This will set the GoEna attribute of all configured GOOSE control blocks
+ * to true. If this method is not called at the startup or reset of the server
+ * then configured GOOSE control blocks keep inactive until a MMS client enables
+ * them by writing to the GOOSE control block.
+ *
+ * \note This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set.
+ *
+ * \param self the instance of IedServer to operate on.
+ */
+LIB61850_API void
+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.
+ *
+ * \note This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set.
+ *
+ * \param self the instance of IedServer to operate on.
+ */
+LIB61850_API void
+IedServer_disableGoosePublishing(IedServer self);
+
+/**
+ * \brief Set the Ethernet interface to be used by GOOSE publishing
+ *
+ * This function can be used to set the GOOSE interface ID. If not used or set to NULL the
+ * default interface ID from stack_config.h is used.
+ *
+ * \note the interface ID is operating system specific!
+ *
+ * \note This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param interfaceId the ID of the ethernet interface to be used for GOOSE publishing
+ */
+LIB61850_API void
+IedServer_setGooseInterfaceId(IedServer self, const char* interfaceId);
+
+/**
+ * \brief Set the Ethernet interface to be used by GOOSE publishing
+ *
+ * This function can be used to set the GOOSE interface ID forG all CBs (parameter ln = NULL) or for
+ * a specific GCB specified by the logical node instance and the GCB name.
+ *
+ * \note This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param ln the logical node that contains the GCB or NULL to set the ethernet interface ID for all GCBs
+ * \param gcbName the name (not object reference!) of the GCB
+ * \param interfaceId the ID of the ethernet interface to be used for GOOSE publishing
+ */
+LIB61850_API void
+IedServer_setGooseInterfaceIdEx(IedServer self, LogicalNode* ln, const char* gcbName, const char* interfaceId);
+
+/**
+ * \brief Enable/disable the use of VLAN tags in GOOSE messages
+ *
+ * This function can be used to enable/disable VLAN tagging for all GCBs (parameter ln = NULL) or for
+ * a specific GCB specified by the logical node instance and the GCB name.
+ *
+ * \note This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set.
+ *
+ * \param self the instance of IedServer to operate on
+ * \param ln the logical node that contains the GCB or NULL to enable/disable VLAN tagging for all GCBs
+ * \param gcbName the name (not object reference!) of the GCB
+ * \param useVlanTag true to enable VLAN tagging, false otherwise
+ */
+LIB61850_API void
+IedServer_useGooseVlanTag(IedServer self, LogicalNode* ln, const char* gcbName, bool useVlanTag);
+
+/**
+ * \brief Set the time quality for all timestamps internally generated by this IedServer instance
+ * 
+ * You can call this function during the initialization of the server or whenever a time quality
+ * flag has to be updated (on clock failure or change of time synchronization state).
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param leapSecondKnown set/unset leap seconds known flag
+ * \param clockFailure set/unset clock failure flag
+ * \param clockNotSynchronized set/unset clock not synchronized flag
+ * \param subsecondPrecision set the subsecond precision (number of significant bits of the fractionOfSecond part of the time stamp)
+ */
+LIB61850_API void
+IedServer_setTimeQuality(IedServer self, bool leapSecondKnown, bool clockFailure, bool clockNotSynchronized, int subsecondPrecision);
+
+/**@}*/
+
+/**
+ * @defgroup IEC61850_SERVER_CONNECTION_HANDLING Connection handling and client authentication
+ *
+ * \brief Functions and callbacks to control client access and connection handling.
+ *
+ * @{
+ */
+
+/**
+ * \brief set the authenticator for this server
+ *
+ * This function sets a user specified authenticator that is used to identify and authenticate clients that
+ * wants to connect. The authenticator is called on each connection attempt. Depending on the return value
+ * of the authenticator the client connection is accepted or refused. If no authenticator is set all client
+ * connections are accepted.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param authenticator the user provided authenticator callback
+ * \param authenticatorParameter user provided parameter that is passed to the authenticator
+ */
+LIB61850_API void
+IedServer_setAuthenticator(IedServer self, AcseAuthenticator authenticator, void* authenticatorParameter);
+
+/**
+ * \brief get the peer address of this connection as string
+ *
+ * \note the returned string is only valid as long as the client connection exists. It is save to use
+ * the string inside of the connection indication callback function.
+ *
+ * \param self the ClientConnection instance
+ * \return peer address as C string.
+ */
+LIB61850_API const char*
+ClientConnection_getPeerAddress(ClientConnection self);
+
+/**
+ * \brief get the local address of this connection as string
+ *
+ * \note the returned string is only valid as long as the client connection exists. It is save to use
+ * the string inside of the connection indication callback function.
+ *
+ * \param self the ClientConnection instance
+ * \return local address as C string.
+ */
+LIB61850_API const char*
+ClientConnection_getLocalAddress(ClientConnection self);
+
+/**
+ * \brief Get the security token associated with this connection
+ *
+ * The security token is an opaque handle that is associated with the connection. It is provided by the
+ * authenticator (if one is present). If no security token is used then this function returns NULL
+ *
+ * \param self the ClientConnection instance
+ *
+ * \return the security token or NULL
+ */
+LIB61850_API void*
+ClientConnection_getSecurityToken(ClientConnection self);
+
+/**
+ * \brief User provided callback function that is invoked whenever a new client connects or an existing connection is closed
+ *        or detected as lost.
+ *
+ * \param self the instance of IedServer where the connection event occured.
+ * \param connection the new or closed client connect object
+ * \param connected true if a new connection is indicated, false if the connection has been closed or detected as lost.
+ * \param parameter a user provided parameter
+ */
+typedef void (*IedConnectionIndicationHandler) (IedServer self, ClientConnection connection, bool connected, void* parameter);
+
+/**
+ * \brief set a callback function that will be called on connection events (open or close).
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param handler the user provided callback function
+ * \param parameter a user provided parameter that is passed to the callback function.
+ */
+LIB61850_API void
+IedServer_setConnectionIndicationHandler(IedServer self, IedConnectionIndicationHandler handler, void* parameter);
+
+/**
+ * \brief Ignore all requests from clients (for testing purposes)
+ *
+ * NOTE: This function will block all client requests on MMS layer
+ * 
+ * \param self the instance of IedServer to configure.
+ * \param enable when true all requests from clients will be ignored
+ */
+void
+IedServer_ignoreClientRequests(IedServer self, bool enable);
+
+/**@}*/
+
+/**
+ * @defgroup IEC61850_SERVER_DATA_MODEL_ACCESS Data model access and data update
+ *
+ * \brief Functions to access and update the data model of an IEC 61850 server instance.
+ *
+ * @{
+ */
+
+/**
+ * \brief Lock the data model for data update.
+ *
+ * This function should be called before the data model is updated.
+ * After updating the data model the function \ref IedServer_unlockDataModel should be called.
+ * Client requests will be postponed until the lock is removed.
+ *
+ * NOTE: This method should never be called inside of a library callback function. In the context of
+ * a library callback the data model is always already locked! Calling this function inside of a
+ * library callback may lead to a deadlock condition.
+ *
+ * \param self the instance of IedServer to operate on.
+ */
+LIB61850_API void
+IedServer_lockDataModel(IedServer self);
+
+/**
+ * \brief Unlock the data model and process pending client requests.
+ *
+ * NOTE: This method should never be called inside of a library callback function. In the context of
+ * a library callback the data model is always already locked!
+ *
+ * \param self the instance of IedServer to operate on.
+ */
+LIB61850_API void
+IedServer_unlockDataModel(IedServer self);
+
+/**
+ * \brief Get data attribute value
+ *
+ * Get the MmsValue object of an MMS Named Variable that is part of the device model.
+ * You should not manipulate the received object directly. Instead you should use
+ * the IedServer_updateValue method.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param dataAttribute the data attribute handle
+ *
+ * \return MmsValue object of the MMS Named Variable or NULL if the value does not exist.
+ */
+LIB61850_API MmsValue*
+IedServer_getAttributeValue(IedServer self, DataAttribute* dataAttribute);
+
+/**
+ * \brief Get data attribute value of a boolean data attribute
+ *
+ * Get the value of a data attribute of type MMS_BOOLEAN. If the data attribute
+ * is of a different type the returned value is undefined.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param dataAttribute the data attribute handle
+ *
+ * \return true or false
+ */
+LIB61850_API bool
+IedServer_getBooleanAttributeValue(IedServer self, const DataAttribute* dataAttribute);
+
+/**
+ * \brief Get data attribute value of an integer data attribute
+ *
+ * Get the value of a data attribute of type MMS_INTEGER. If the data attribute
+ * is of a different type the returned value is undefined.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param dataAttribute the data attribute handle
+ *
+ * \return the value as 32 bit integer
+ */
+LIB61850_API int32_t
+IedServer_getInt32AttributeValue(IedServer self, const DataAttribute* dataAttribute);
+
+/**
+ * \brief Get data attribute value of an integer data attribute
+ *
+ * Get the value of a data attribute of type MMS_INTEGER. If the data attribute
+ * is of a different type the returned value is undefined.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param dataAttribute the data attribute handle
+ *
+ * \return the value as 64 bit integer
+ */
+LIB61850_API int64_t
+IedServer_getInt64AttributeValue(IedServer self, const DataAttribute* dataAttribute);
+
+/**
+ * \brief Get data attribute value of an unsigned integer data attribute
+ *
+ * Get the value of a data attribute of type MMS_UNSIGNED. If the data attribute
+ * is of a different type the returned value is undefined.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param dataAttribute the data attribute handle
+ *
+ * \return the value as 32 bit unsigned integer
+ */
+LIB61850_API uint32_t
+IedServer_getUInt32AttributeValue(IedServer self, const DataAttribute* dataAttribute);
+
+/**
+ * \brief Get data attribute value of a floating point data attribute
+ *
+ * Get the value of a data attribute of type MMS_FLOAT. If the data attribute
+ * is of a different type the returned value is undefined.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param dataAttribute the data attribute handle
+ *
+ * \return the value as 32 bit float
+ */
+LIB61850_API float
+IedServer_getFloatAttributeValue(IedServer self, const DataAttribute* dataAttribute);
+
+/**
+ * \brief Get data attribute value of a UTC time data attribute
+ *
+ * Get the value of a data attribute of type MMS_UTC_TIME. If the data attribute
+ * is of a different type the returned value is undefined.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param dataAttribute the data attribute handle
+ *
+ * \return the value as 64 bit unsigned integer representing the time in milliseconds since Epoch
+ */
+LIB61850_API uint64_t
+IedServer_getUTCTimeAttributeValue(IedServer self, const DataAttribute* dataAttribute);
+
+/**
+ * \brief Get data attribute value of a bit string data attribute as integer value
+ *
+ * Get the value of a data attribute of type MMS_BIT_STRING. If the data attribute
+ * is of a different type the returned value is undefined.
+ * NOTE: The returned integer is determined by calculating the according the follwing
+ * formula: bit0^0 + bit1^1 + ... + bitn^n
+ * It is not specified in the IEC 61850 specifications how a bit string can be interpreted
+ * as an integer. Therefore this function has to be used with care.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param dataAttribute the data attribute handle
+ *
+ * \return the value a 32 bit integer.
+ */
+LIB61850_API uint32_t
+IedServer_getBitStringAttributeValue(IedServer self, const DataAttribute* dataAttribute);
+
+/**
+ * \brief Get data attribute value of a string type data attribute
+ *
+ * Get the value of a data attribute of type MMS_STRING or MMS_VISIBLE_STRING. If the data attribute
+ * is of a different type the returned value is undefined.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param dataAttribute the data attribute handle
+ *
+ * \return the value as a C string (null terminated string)
+ */
+LIB61850_API const char*
+IedServer_getStringAttributeValue(IedServer self, const DataAttribute* dataAttribute);
+
+/**
+ * \brief Get the MmsValue object related to a functional constrained data object (FCD)
+ *
+ * Get the MmsValue from the server cache that is associated with the Functional Constrained Data (FCD)
+ * object that is specified by the DataObject and the given Function Constraint (FC).
+ * Accessing the value will directly influence the values presented by the server. This kind of direct
+ * access will also bypass the report notification mechanism (i.e. changes will not cause a report!).
+ * Therefore this function should be used with care. It could be useful to access arrays of DataObjects.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param dataObject the data object to specify the FCD
+ * \param fc the FC to specify the FCD
+ *
+ * \return MmsValue object cached by the server.
+ */
+LIB61850_API MmsValue*
+IedServer_getFunctionalConstrainedData(IedServer self, DataObject* dataObject, FunctionalConstraint fc);
+
+/**
+ * \brief Update the MmsValue object of an IEC 61850 data attribute.
+ *
+ * The data attribute handle of type DataAttribute* are imported with static_model.h in the case when
+ * the static data model is used.
+ * You should use this function instead of directly operating on the MmsValue instance
+ * that is hold by the MMS server. Otherwise the IEC 61850 server is not aware of the
+ * changed value and will e.g. not generate a report.
+ *
+ * This function will also check if a trigger condition is satisfied in the case when a report or GOOSE
+ * control block is enabled.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param dataAttribute the data attribute handle
+ * \param value MmsValue object used to update the value cached by the server.
+ */
+LIB61850_API void
+IedServer_updateAttributeValue(IedServer self, DataAttribute* dataAttribute, MmsValue* value);
+
+/**
+ * \brief Update the value of an IEC 61850 float data attribute.
+ *
+ * Update the value of a float data attribute without handling with MmsValue instances.
+ *
+ * This function will also check if a trigger condition is satisfied in the case when a report or GOOSE
+ * control block is enabled.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param dataAttribute the data attribute handle
+ * \param value the new float value of the data attribute.
+ */
+LIB61850_API void
+IedServer_updateFloatAttributeValue(IedServer self, DataAttribute* dataAttribute, float value);
+
+/**
+ * \brief Update the value of an IEC 61850 integer32 data attribute.
+ *
+ * Update the value of a integer data attribute without handling with MmsValue instances.
+ *
+ * This function will also check if a trigger condition is satisfied in the case when a report or GOOSE
+ * control block is enabled.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param dataAttribute the data attribute handle
+ * \param value the new integer value of the data attribute.
+ */
+LIB61850_API void
+IedServer_updateInt32AttributeValue(IedServer self, DataAttribute* dataAttribute, int32_t value);
+
+/**
+ * \brief Update the value of an IEC 61850 Dbpos (double point/position) data attribute.
+ *
+ * Update the value of a integer data attribute without handling with MmsValue instances.
+ *
+ * This function will also check if a trigger condition is satisfied in the case when a report or GOOSE
+ * control block is enabled.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param dataAttribute the data attribute handle
+ * \param value the new Dbpos value of the data attribute.
+ */
+LIB61850_API void
+IedServer_updateDbposValue(IedServer self, DataAttribute* dataAttribute, Dbpos value);
+
+/**
+ * \brief Update the value of an IEC 61850 integer64 data attribute (like BCR actVal)
+ *
+ * Update the value of a integer data attribute without handling with MmsValue instances.
+ *
+ * This function will also check if a trigger condition is satisfied in the case when a report or GOOSE
+ * control block is enabled.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param dataAttribute the data attribute handle
+ * \param value the new 64 bit integer value of the data attribute.
+ */
+LIB61850_API void
+IedServer_updateInt64AttributeValue(IedServer self, DataAttribute* dataAttribute, int64_t value);
+
+/**
+ * \brief Update the value of an IEC 61850 unsigned integer data attribute.
+ *
+ * Update the value of a unsigned data attribute without handling with MmsValue instances.
+ *
+ * This function will also check if a trigger condition is satisfied in the case when a report or GOOSE
+ * control block is enabled.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param dataAttribute the data attribute handle
+ * \param value the new unsigned integer value of the data attribute.
+ */
+LIB61850_API void
+IedServer_updateUnsignedAttributeValue(IedServer self, DataAttribute* dataAttribute, uint32_t value);
+
+/**
+ * \brief Update the value of an IEC 61850 bit string data attribute.
+ *
+ * Update the value of a bit string data attribute without handling with MmsValue instances.
+ *
+ * This function will also check if a trigger condition is satisfied in the case when a report or GOOSE
+ * control block is enabled.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param dataAttribute the data attribute handle
+ * \param value the new bit string integer value of the data attribute.
+ */
+LIB61850_API void
+IedServer_updateBitStringAttributeValue(IedServer self, DataAttribute* dataAttribute, uint32_t value);
+
+/**
+ * \brief Update the value of an IEC 61850 boolean data attribute.
+ *
+ * Update the value of a boolean data attribute without handling with MmsValue instances.
+ *
+ * This function will also check if a trigger condition is satisfied in the case when a report or GOOSE
+ * control block is enabled.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param dataAttribute the data attribute handle
+ * \param value the new boolean value of the data attribute.
+ */
+LIB61850_API void
+IedServer_updateBooleanAttributeValue(IedServer self, DataAttribute* dataAttribute, bool value);
+
+/**
+ * \brief Update the value of an IEC 61850 visible string data attribute.
+ *
+ * Update the value of a visible string data attribute without handling MmsValue instances.
+ *
+ * This function will also check if a trigger condition is satisfied in the case when a report or GOOSE
+ * control block is enabled.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param dataAttribute the data attribute handle
+ * \param value the new visible string value of the data attribute.
+ */
+LIB61850_API void
+IedServer_updateVisibleStringAttributeValue(IedServer self, DataAttribute* dataAttribute, char *value);
+
+/**
+ * \brief Update the value of an IEC 61850 UTC time (timestamp) data attribute.
+ *
+ * Update the value of a UTC time data attribute without handling MmsValue instances.
+ *
+ * This function will also check if a trigger condition is satisfied in the case when a report or GOOSE
+ * control block is enabled.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param dataAttribute the data attribute handle
+ * \param value the new UTC time value of the data attribute as a ms timestamp
+ */
+LIB61850_API void
+IedServer_updateUTCTimeAttributeValue(IedServer self, DataAttribute* dataAttribute, uint64_t value);
+
+/**
+ * \brief Update the value of an IEC 61850 UTC time (timestamp) data attribute.
+ *
+ * Update the value of a UTC time data attribute without handling MmsValue instances.
+ *
+ * This function will also check if a trigger condition is satisfied in the case when a report or GOOSE
+ * control block is enabled.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param dataAttribute the data attribute handle
+ * \param value the new UTC time value of the data attribute as a Timestamp
+ */
+LIB61850_API void
+IedServer_updateTimestampAttributeValue(IedServer self, DataAttribute* dataAttribute, Timestamp* timestamp);
+
+/**
+ * \brief Update a quality ("q") IEC 61850 data attribute.
+ *
+ * This is a specialized function to handle Quality data attributes. It can be used instead of the more
+ * generic IedServer_updateAttributeValue function.
+ *
+ * This function will also check if the quality change  (qchg) trigger condition is satisfied and inform a
+ * report control block accordingly.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param dataAttribute the data attribute handle
+ * \param quality the new quality value
+ *
+ */
+LIB61850_API void
+IedServer_updateQuality(IedServer self, DataAttribute* dataAttribute, Quality quality);
+
+/**@}*/
+
+/**
+ * @defgroup IEC61850_SERVER_SETTING_GROUPS Server side setting group handling
+ *
+ * \brief Functions and callbacks to handle setting groups on the server side.
+ *
+ * @{
+ */
+
+/**
+ * \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
+ */
+LIB61850_API 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
+ */
+LIB61850_API 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.
+ */
+LIB61850_API 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.
+ */
+LIB61850_API 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.
+ */
+LIB61850_API void
+IedServer_setEditSettingGroupConfirmationHandler(IedServer self, SettingGroupControlBlock* sgcb,
+        EditSettingGroupConfirmationHandler handler, void* parameter);
+
+/**@}*/
+
+/**
+ * @defgroup IEC61850_SERVER_CONTROL Server side control model handling
+ *
+ * \brief Functions and callbacks to handle control model related operations on the server side
+ *
+ * @{
+ */
+
+/**
+ * \brief result code for ControlPerformCheckHandler
+ */
+typedef enum {
+    CONTROL_ACCEPTED = -1, /**< check passed */
+    CONTROL_WAITING_FOR_SELECT = 0, /**< select operation in progress - handler will be called again later */
+    CONTROL_HARDWARE_FAULT = 1, /**< check failed due to hardware fault */
+    CONTROL_TEMPORARILY_UNAVAILABLE = 2, /**< control is already selected or operated */
+    CONTROL_OBJECT_ACCESS_DENIED = 3, /**< check failed due to access control reason - access denied for this client or state */
+    CONTROL_OBJECT_UNDEFINED = 4, /**< object not visible in this security context ??? */
+    CONTROL_VALUE_INVALID = 11 /**< ctlVal out of range */
+} 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;
+
+typedef void* ControlAction;
+
+/**
+ * \brief Sets the error code for the next command termination or application error message
+ *
+ * \param self the control action instance
+ * \param error the error code
+ */
+LIB61850_API void
+ControlAction_setError(ControlAction self, ControlLastApplError error);
+
+/**
+ * \brief Sets the add cause for the next command termination or application error message
+ *
+ * \param self the control action instance
+ * \param addCause the additional cause
+ */
+LIB61850_API void
+ControlAction_setAddCause(ControlAction self, ControlAddCause addCause);
+
+/**
+ * \brief Gets the originator category provided by the client
+ *
+ * \param self the control action instance
+ *
+ * \return the originator category
+ */
+LIB61850_API int
+ControlAction_getOrCat(ControlAction self);
+
+/**
+ * \brief Gets the originator identifier provided by the client
+ *
+ * \param self the control action instance
+ *
+ * \return the originator identifier
+ */
+LIB61850_API uint8_t*
+ControlAction_getOrIdent(ControlAction self, int* orIdentSize);
+
+/**
+ * \brief Get the ctlNum attribute send by the client
+ *
+ * \param self the control action instance
+ *
+ * \return the ctlNum value
+ */
+LIB61850_API int
+ControlAction_getCtlNum(ControlAction self);
+
+/**
+ * \brief Gets the synchroCheck bit provided by the client
+ *
+ * \param self the control action instance
+ *
+ * \return the synchroCheck bit
+ */
+LIB61850_API bool
+ControlAction_getSynchroCheck(ControlAction self);
+
+/**
+ * \brief Gets the interlockCheck bit provided by the client
+ *
+ * \param self the control action instance
+ *
+ * \return the interlockCheck bit
+ */
+LIB61850_API bool
+ControlAction_getInterlockCheck(ControlAction self);
+
+/**
+ * \brief Check if the control callback is called by a select or operate command
+ *
+ * \param self the control action instance
+ *
+ * \return true, when called by select, false for operate
+ */
+LIB61850_API bool
+ControlAction_isSelect(ControlAction self);
+
+/**
+ * \brief Gets the client object associated with the client that caused the control action
+ *
+ * \param self the control action instance
+ *
+ * \return client connection instance
+ */
+LIB61850_API ClientConnection
+ControlAction_getClientConnection(ControlAction self);
+
+/**
+ * \brief Gets the control object that is subject to this action
+ *
+ * \param self the control action instance
+ *
+ * \return the controllable data object instance
+ */
+LIB61850_API DataObject*
+ControlAction_getControlObject(ControlAction self);
+
+/**
+ * \brief Gets the time of the control (attribute "operTm"), if it's a timeActivatedControl, returns 0, if it's not.
+ *
+ * \param self the control action instance
+ *
+ * \return the controllable data object instance
+ */
+LIB61850_API uint64_t
+ControlAction_getControlTime(ControlAction self);
+
+/**
+ * \brief Gets the time (attribute "T") of the last received control action (Oper or Select)
+ *
+ * \param self the control action instance
+ *
+ * \return the time of the last received control action
+ */
+LIB61850_API Timestamp*
+ControlAction_getT(ControlAction self);
+
+/**
+ * \brief Control model callback to perform the static tests (optional).
+ *
+ * \note Signature changed in version 1.4!
+ *
+ * User provided callback function for the control model. It will be invoked after
+ * a control operation has been invoked by the client. This callback function is
+ * intended to perform the static tests. It should check if the interlock conditions
+ * are met if the interlockCheck parameter is true.
+ * This handler can also be check if the client has the required permissions to execute the
+ * operation and allow or deny the operation accordingly.
+ *
+ * \param action the control action parameter that provides access to additional context information
+ * \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 interlockCheck the interlockCheck parameter provided by the client
+ *
+ * \return CONTROL_ACCEPTED if the static tests had been successful, one of the error codes otherwise
+ */
+typedef CheckHandlerResult (*ControlPerformCheckHandler) (ControlAction action, void* parameter, MmsValue* ctlVal, bool test, bool interlockCheck);
+
+/**
+ * \brief Control model callback to perform the dynamic tests (optional).
+ *
+ * \note  Signature changed in version 1.4!
+ *
+ * User provided callback function for the control model. It will be invoked after
+ * 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 action the control action parameter that provides access to additional context information
+ * \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 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 ControlHandlerResult (*ControlWaitForExecutionHandler) (ControlAction action, void* parameter, MmsValue* ctlVal, bool test, bool synchroCheck);
+
+/**
+ * \brief Control model callback to actually perform the control operation.
+ *
+ * \note Signature changed in version 1.4!
+ *
+ * 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 action the control action parameter that provides access to additional context information
+ * \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 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 ControlHandlerResult (*ControlHandler) (ControlAction action, void* parameter, MmsValue* ctlVal, bool test);
+
+/**
+ * \brief Reason why a select state of a control object changed
+ */
+typedef enum {
+    SELECT_STATE_REASON_SELECTED, /**< control has been selected */
+    SELECT_STATE_REASON_CANCELED, /**< cancel received for the control */
+    SELECT_STATE_REASON_TIMEOUT,  /**< unselected due to timeout (sboTimeout) */
+    SELECT_STATE_REASON_OPERATED, /**< unselected due to successful operate */
+    SELECT_STATE_REASON_OPERATE_FAILED, /**< unselected due to failed operate */
+    SELECT_STATE_REASON_DISCONNECTED /**< unselected due to disconnection of selecting client */
+} SelectStateChangedReason;
+
+/**
+ * \brief Control model callback that is called when the select state of a control changes
+ *
+ * \note New in version 1.5
+ *
+ * \param action the control action parameter that provides access to additional context information
+ * \param parameter the parameter that was specified when setting the control handler
+ * \param isSelected true when the control is selected, false otherwise
+ * \param reason reason why the select state changed
+ */
+typedef void (*ControlSelectStateChangedHandler) (ControlAction action, void* parameter, bool isSelected, SelectStateChangedReason reason);
+
+/**
+ * \brief Set control handler for controllable data object
+ *
+ * This functions sets a user provided control handler for a data object. The data object
+ * has to be an instance of a controllable CDC (Common Data Class) like e.g. SPC, DPC or APC.
+ * The control handler is a callback function that will be called by the IEC server when a
+ * client invokes a control operation on the data object.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param node the controllable data object handle
+ * \param handler a callback function of type ControlHandler
+ * \param parameter a user provided parameter that is passed to the control handler.
+ */
+LIB61850_API void
+IedServer_setControlHandler(IedServer self, DataObject* node, ControlHandler handler, void* parameter);
+
+/**
+ * \brief Set a handler for a controllable data object to perform operative tests
+ *
+ * This functions sets a user provided handler that should perform the operative tests for a control operation.
+ * Setting this handler is not required. If not set the server assumes that the checks will always be successful.
+ * The handler has to return true upon a successful test of false if the test fails. In the later case the control
+ * operation will be aborted.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param node the controllable data object handle
+ * \param handler a callback function of type ControlHandler
+ * \param parameter a user provided parameter that is passed to the control handler.
+ *
+ */
+LIB61850_API void
+IedServer_setPerformCheckHandler(IedServer self, DataObject* node, ControlPerformCheckHandler handler, void* parameter);
+
+/**
+ * \brief Set a handler for a controllable data object to perform dynamic tests
+ *
+ * This functions sets a user provided handler that should perform the dynamic tests for a control operation.
+ * Setting this handler is not required. If not set the server assumes that the checks will always be successful.
+ * The handler has to return true upon a successful test of false if the test fails. In the later case the control
+ * operation will be aborted.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param node the controllable data object handle
+ * \param handler a callback function of type ControlHandler
+ * \param parameter a user provided parameter that is passed to the control handler.
+ *
+ */
+LIB61850_API void
+IedServer_setWaitForExecutionHandler(IedServer self, DataObject* node, ControlWaitForExecutionHandler handler, void* parameter);
+
+
+/**
+ * \brief Set a callback handler for a controllable data object to track select state changes
+ *
+ * The callback is called whenever the select state of a control changes. Reason for changes can be:
+ * - a successful select or select-with-value by a client
+ * - select timeout
+ * - operate or failed operate
+ * - cancel request by a client
+ * - the client that selected the control has been disconnected
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param node the controllable data object handle
+ * \param handler a callback function of type ControlHandler
+ * \param parameter a user provided parameter that is passed to the callback handler.
+ */
+LIB61850_API void
+IedServer_setSelectStateChangedHandler(IedServer self, DataObject* node, ControlSelectStateChangedHandler handler, void* parameter);
+
+/**
+ * \brief Update the control model for the specified controllable data object with the given value and
+ *        update "ctlModel" attribute value.
+ *
+ * \note The corresponding control structures for the control model have to be present in the data model!
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param ctlObject the controllable data object handle
+ * \param value the new control model value
+ */
+LIB61850_API void
+IedServer_updateCtlModel(IedServer self, DataObject* ctlObject, ControlModel value);
+
+/**@}*/
+
+/**
+ * @defgroup IEC61850_SERVER_RCB Server side report control block (RCB) handling
+ *
+ * \brief Functions and callbacks to handle report control blocks (RCBs) on the server side
+ *
+ * @{
+ */
+
+typedef enum {
+    RCB_EVENT_GET_PARAMETER, /* << parameter read by client (not implemented) */
+    RCB_EVENT_SET_PARAMETER, /* << parameter set by client */
+    RCB_EVENT_UNRESERVED,    /* << RCB reservation canceled */
+    RCB_EVENT_RESERVED,      /* << RCB reserved */
+    RCB_EVENT_ENABLE,        /* << RCB enabled */
+    RCB_EVENT_DISABLE,       /* << RCB disabled */
+    RCB_EVENT_GI,            /* << GI report triggered */
+    RCB_EVENT_PURGEBUF,      /* << Purge buffer procedure executed */
+    RCB_EVENT_OVERFLOW,      /* << Report buffer overflow */
+    RCB_EVENT_REPORT_CREATED /* << A new report was created and inserted into the buffer */
+} IedServer_RCBEventType;
+
+/**
+ * \brief Callback that is called in case of RCB event
+ *
+ * \param parameter user provided parameter
+ * \param rcb affected report control block
+ * \param connection client connection that is involved
+ * \param event event type
+ * \param parameterName name of the parameter in case of RCB_EVENT_SET_PARAMETER
+ * \param serviceError service error in case of RCB_EVENT_SET_PARAMETER
+ */
+typedef void (*IedServer_RCBEventHandler) (void* parameter, ReportControlBlock* rcb, ClientConnection connection, IedServer_RCBEventType event, const char* parameterName, MmsDataAccessError serviceError);
+
+/**
+ * \brief Set a handler for report control block (RCB) events
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param handler the event handler to be used
+ * \param parameter a user provided parameter that is passed to the handler.
+ */
+LIB61850_API void
+IedServer_setRCBEventHandler(IedServer self, IedServer_RCBEventHandler handler, void* parameter);
+
+/**@}*/
+
+/**
+ * @defgroup IEC61850_SERVER_SVCB Server side sampled values control block (SVCB) handling
+ *
+ * \brief Functions and callbacks to handle sampled values control blocks on the server side
+ *
+ * @{
+ */
+
+/** Control block has been enabled by client */
+#define IEC61850_SVCB_EVENT_ENABLE 1
+
+/** Control block has been disabled by client */
+#define IEC61850_SVCB_EVENT_DISABLE 0
+
+/**
+ * \brief callback handler for SVCB events.
+ *
+ * \param svcb the related SVCB instance
+ * \param the event type
+ * \param user defined parameter
+ */
+typedef void (*SVCBEventHandler) (SVControlBlock* svcb, int event, void* parameter);
+
+/**
+ * \brief Set a handler for SVCB control block events (enable/disable)
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param svcb the SVCB control block instance
+ * \param handler the event handler to be used
+ * \param parameter a user provided parameter that is passed to the handler.
+ */
+LIB61850_API void
+IedServer_setSVCBHandler(IedServer self, SVControlBlock* svcb, SVCBEventHandler handler, void* parameter);
+
+/**@}*/
+
+/**
+ * @defgroup IEC61850_SERVER_GOCB Server side GOOSE control block (GoCB) handling
+ *
+ * \brief Functions and callbacks to handle GOOSE control blocks (GoCBs) on the server side
+ *
+ * @{
+ */
+
+typedef struct sMmsGooseControlBlock* MmsGooseControlBlock;
+
+/** Control block has been enabled by client */
+#define IEC61850_GOCB_EVENT_ENABLE 1
+
+/** Control block has been disabled by client */
+#define IEC61850_GOCB_EVENT_DISABLE 0
+
+typedef void (*GoCBEventHandler) (MmsGooseControlBlock goCb, int event, void* parameter);
+
+/**
+ * \brief Set a callback handler for GoCB events (enabled/disabled)
+ *
+ * The callback handler is called whenever a GOOSE control block is enabled or disabled.
+ * It can be used to integrate the external GOOSE publisher with the IEC 61850/MMS server.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param handler the callback handler
+ * \param parameter user provided parameter that is passed to the callback handler
+ */
+LIB61850_API void
+IedServer_setGoCBHandler(IedServer self, GoCBEventHandler handler, void* parameter);
+
+LIB61850_API char*
+MmsGooseControlBlock_getName(MmsGooseControlBlock self);
+
+LIB61850_API LogicalNode*
+MmsGooseControlBlock_getLogicalNode(MmsGooseControlBlock self);
+
+LIB61850_API DataSet*
+MmsGooseControlBlock_getDataSet(MmsGooseControlBlock self);
+
+LIB61850_API bool
+MmsGooseControlBlock_getGoEna(MmsGooseControlBlock self);
+
+LIB61850_API int
+MmsGooseControlBlock_getMinTime(MmsGooseControlBlock self);
+
+LIB61850_API int
+MmsGooseControlBlock_getMaxTime(MmsGooseControlBlock self);
+
+LIB61850_API bool
+MmsGooseControlBlock_getFixedOffs(MmsGooseControlBlock self);
+
+LIB61850_API bool
+MmsGooseControlBlock_getNdsCom(MmsGooseControlBlock self);
+
+/**@}*/
+
+/**
+ * @defgroup IEC61850_SERVER_EXTERNAL_ACCESS Handle external access to data model and access control
+ *
+ * \brief Functions and callbacks to handle and restrict external access to the data model and services
+ *
+ * This module provides functions and callbacks to restrict external access to the data model and services of the IEC 61850 server.
+ * They can be used to implement access control mechanisms like role based access control (RBAC) and to restrict access to specific
+ * data objects and data attributes.
+ *
+ * @{
+ */
+
+/***************************************************************************
+ * Access control
+ **************************************************************************/
+
+/**
+ * \brief callback handler to intercept/control client write access to data attributes
+ *
+ * User provided callback function to intercept/control MMS client access to
+ * IEC 61850 data attributes. The application can install the same handler
+ * multiple times and distinguish data attributes by the dataAttribute parameter.
+ * This handler can be used to perform write access control do data attributes.
+ * One application can be to allow write access only from a specific client. Another
+ * application could be to check if the value is in the allowed range before the write
+ * is accepted.
+ * When the callback returns DATA_ACCESS_ERROR_SUCCESS the write access is accepted and the stack will
+ * update the value automatically.
+ * When the callback returns DATA_ACCESS_ERROR_SUCCESS_NO_UPDATE the write access is accepted but the
+ * stack will not update the value automatically.
+ *
+ * \param the data attribute that has been written by an MMS client.
+ * \param the value the client want to write to the data attribute
+ * \param connection the connection object of the client connection that invoked the write operation
+ * \param parameter the user provided parameter
+ *
+ * \return DATA_ACCESS_ERROR_SUCCESS, or DATA_ACCESS_ERROR_SUCCESS_NO_UPDATE if access is accepted, DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED if access is denied.
+ */
+typedef MmsDataAccessError
+(*WriteAccessHandler) (DataAttribute* dataAttribute, MmsValue* value, ClientConnection connection, void* parameter);
+
+/**
+ * \brief Install a WriteAccessHandler for a data attribute.
+ *
+ * This instructs the server to monitor write attempts by MMS clients to specific
+ * data attributes. If a client tries to write to the monitored data attribute the
+ * handler is invoked. The handler can decide if the write access will be allowed
+ * or denied. If a WriteAccessHandler is set for a specific data attribute - the
+ * default write access policy will not be performed for that data attribute.
+ *
+ * \note If the data attribute has sub data attributes, the WriteAccessHandler is not
+ * set for the sub data attributes and will not be called when the sub data attribute is
+ * written directly!
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param dataAttribute the data attribute to monitor
+ * \param handler the callback function that is invoked if a client tries to write to
+ *        the monitored data attribute.
+ * \param parameter a user provided parameter that is passed to the WriteAccessHandler when called.
+ */
+LIB61850_API void
+IedServer_handleWriteAccess(IedServer self, DataAttribute* dataAttribute,
+        WriteAccessHandler handler, void* parameter);
+
+/**
+ * \brief Install a WriteAccessHandler for a data attribute and for all sub data attributes
+ *
+ * This instructs the server to monitor write attempts by MMS clients to specific
+ * data attributes. If a client tries to write to the monitored data attribute the
+ * handler is invoked. The handler can decide if the write access will be allowed
+ * or denied. If a WriteAccessHandler is set for a specific data attribute - the
+ * default write access policy will not be performed for that data attribute.
+ *
+ * When the data attribute is a complex attribute then the handler will also be installed
+ * for all sub data attributes. When the data attribute is a basic data attribute then
+ * this function behaves like \ref IedServer_handleWriteAccess.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param dataAttribute the data attribute to monitor
+ * \param handler the callback function that is invoked if a client tries to write to
+ *        the monitored data attribute.
+ * \param parameter a user provided parameter that is passed to the WriteAccessHandler when called.
+ */
+LIB61850_API void
+IedServer_handleWriteAccessForComplexAttribute(IedServer self, DataAttribute* dataAttribute,
+        WriteAccessHandler handler, void* parameter);
+
+/**
+ * \brief Install a WriteAccessHandler for all data attributes of a data object with a specific FC
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param dataObject the data object to monitor
+ * \param fc the functional constraint to monitor
+ * \param handler the callback function that is invoked if a client tries to write to
+ *       the monitored data attribute.
+ * \param parameter a user provided parameter that is passed to the WriteAccessHandler when called.
+*/
+LIB61850_API void
+IedServer_handleWriteAccessForDataObject(IedServer self, DataObject* dataObject, FunctionalConstraint fc, WriteAccessHandler handler, void* parameter);
+
+typedef enum {
+    ACCESS_POLICY_ALLOW,
+    ACCESS_POLICY_DENY
+} AccessPolicy;
+
+/**
+ * \brief Change the default write access policy for functional constraint data with a specific FC.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param fc the FC for which to change the default write access policy.
+ * \param policy the new policy to apply.
+ *
+ */
+LIB61850_API void
+IedServer_setWriteAccessPolicy(IedServer self, FunctionalConstraint fc, AccessPolicy policy);
+
+/**
+ * \brief callback handler to control client read access to data attributes
+ *
+ * User provided callback function to control MMS client read access to IEC 61850
+ * data objects. The application is to allow read access to data objects for specific clients only.
+ * It can be used to implement a role based access control (RBAC).
+ *
+ * \param ld the logical device the client wants to access
+ * \param ln the logical node the client wants to access
+ * \param dataObject the data object the client wants to access
+ * \param fc the functional constraint of the access
+ * \param connection the client connection that causes the access
+ * \param parameter the user provided parameter
+ *
+ * \return DATA_ACCESS_ERROR_SUCCESS if access is accepted, DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED if access is denied.
+ */
+typedef MmsDataAccessError
+(*ReadAccessHandler) (LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, FunctionalConstraint fc, ClientConnection connection, void* parameter);
+
+/**
+ * \brief Install the global read access handler
+ *
+ * The read access handler will be called for every read access before the server grants access to the client.
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param handler the callback function that is invoked if a client tries to read a data object.
+ * \param parameter a user provided parameter that is passed to the callback function.
+ */
+LIB61850_API void
+IedServer_setReadAccessHandler(IedServer self, ReadAccessHandler handler, void* parameter);
+
+typedef enum {
+    DATASET_CREATE,
+    DATASET_DELETE,
+    DATASET_READ,
+    DATASET_WRITE,
+    DATASET_GET_DIRECTORY
+} IedServer_DataSetOperation;
+
+/**
+ * \brief Callback that is called when the client is calling a dataset operation (create, delete, read, write, list directory)
+ * 
+ * \note This callback is called before the IedServer_RCBEventHandler and only in case of operations (RCB_EVENT_GET_PARAMETER, RCB_EVENT_SET_PARAMETER, RCB_EVENT_ENABLE
+ * 
+ * \param parameter user provided parameter
+ * \param connection client connection that is involved
+ * \param operation one of the following operation types: DATASET_CREATE, DATASET_DELETE, DATASET_READ, DATASET_WRITE, DATASET_GET_DIRECTORY
+ * 
+ * \return true to allow operation, false to deny operation
+ */
+typedef bool
+(*IedServer_DataSetAccessHandler) (void* parameter, ClientConnection connection, IedServer_DataSetOperation operation, const char* datasetRef);
+
+/**
+ * \brief Set a handler to control access to a dataset (create, delete, read, write, list directory)
+ * 
+ * \param handler the callback handler to be used
+ * \param parameter a user provided parameter that is passed to the handler.
+ */
+LIB61850_API void
+IedServer_setDataSetAccessHandler(IedServer self, IedServer_DataSetAccessHandler handler, void* parameter);
+
+typedef enum {
+    DIRECTORY_CAT_LD_LIST,
+    DIRECTORY_CAT_DATA_LIST,
+    DIRECTORY_CAT_DATASET_LIST,
+    DIRECTORY_CAT_LOG_LIST
+} IedServer_DirectoryCategory;
+
+typedef bool
+(*IedServer_DirectoryAccessHandler) (void* parameter, ClientConnection connection, IedServer_DirectoryCategory category, LogicalDevice* logicalDevice);
+
+LIB61850_API void
+IedServer_setDirectoryAccessHandler(IedServer self, IedServer_DirectoryAccessHandler handler, void* parameter);
+
+/**
+ * \brief Callback that is called when a client is invoking a list objects service
+ *
+ * This callback can be used to control the list object access to specific objects and is called for each object that are subject to a client request.
+ *
+ * \param parameter user provided parameter
+ * \param connection client connection that is involved
+ * \param acsiClass the ACSI class of the object
+ * \param ld the logical device of the object
+ * \param ln the logical node of the object
+ * \param objectName the name of the object (e.g. data object name, data set name, log name, RCB name, ...)
+ * \param subObjectName the name of a sub element of an object or NULL
+ * \param fc the functional constraint of the object of IEC61850_FC_NONE when the object has no FC.
+ *
+ * \return true to include the object in the service response, otherwise false
+ */
+typedef bool
+(*IedServer_ListObjectsAccessHandler)(void* parameter, ClientConnection connection, ACSIClass acsiClass, LogicalDevice* ld, LogicalNode* ln, const char* objectName, const char* subObjectName, FunctionalConstraint fc);
+
+/**
+ * \brief Set a handler to control which objects are return by the list objects services
+ *
+ * \param handler the callback handler to be used
+ * \param parameter a user provided parameter that is passed to the handler.
+ */
+LIB61850_API void
+IedServer_setListObjectsAccessHandler(IedServer self, IedServer_ListObjectsAccessHandler handler, void* parameter);
+
+typedef enum {
+    IEC61850_CB_ACCESS_TYPE_READ,
+    IEC61850_CB_ACCESS_TYPE_WRITE
+} IedServer_ControlBlockAccessType;
+
+/**
+ * \brief Callback that is called when a client is invoking a read or write service to a control block or log
+ *
+ * This callback can be used to control the read and write access to control blocks and logs (SGCB, LCBs, URCBs, BRCBs, GoCBs, SVCBs, logs)
+ *
+ * \param parameter user provided parameter
+ * \param connection client connection that is involved
+ * \param acsiClass the ACSI class of the object
+ * \param ld the logical device of the object
+ * \param ln the logical node of the object
+ * \param objectName the name of the object (e.g. data object name, data set name, log name, RCB name, ...)
+ * \param subObjectName the name of a sub element of an object or NULL
+ * \param accessType access type (read=IEC61850_CB_ACCESS_TYPE_READ or write=IEC61850_CB_ACCESS_TYPE_WRITE)
+ *
+ * \return true to include the object in the service response, otherwise false
+ */
+typedef bool
+(*IedServer_ControlBlockAccessHandler)(void* parameter, ClientConnection connection, ACSIClass acsiClass, LogicalDevice* ld, LogicalNode* ln, const char* objectName, const char* subObjectName, IedServer_ControlBlockAccessType accessType);
+
+/**
+ * \brief Set a handler to control read and write access to control blocks and logs
+ *
+ * \param handler the callback handler to be used
+ * \param parameter a user provided parameter that is passed to the handler.
+ */
+LIB61850_API void
+IedServer_setControlBlockAccessHandler(IedServer self, IedServer_ControlBlockAccessHandler handler, void* parameter);
+
+/**
+ * \brief Temporarily ignore read requests (for testing purposes)
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param ignore true to ignore read requests, false to handle read requests.
+*/
+LIB61850_API void
+IedServer_ignoreReadAccess(IedServer self, bool ignore);
+
+/**@}*/
+
+/**@}*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* IED_SERVER_API_H_ */

+ 304 - 0
library/include/libiec61850/iso_connection_parameters.h

@@ -0,0 +1,304 @@
+/*
+ *  iso_connection_parameters.h
+ *
+ *  Copyright 2013-2023 Michael Zillgith
+ *
+ *  This file is part of libIEC61850.
+ *
+ *  libIEC61850 is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  libIEC61850 is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with libIEC61850.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef ISO_CONNECTION_PARAMETERS_H_
+#define ISO_CONNECTION_PARAMETERS_H_
+
+#ifndef CONFIG_MMS_SUPPORT_TLS
+#define CONFIG_MMS_SUPPORT_TLS 0
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "tls_config.h"
+
+/**
+ * \addtogroup mms_client_api_group
+ */
+/**@{*/
+
+
+/**
+ * \brief authentication mechanism used by AcseAuthenticator
+ */
+typedef enum
+{
+    /** Neither ACSE nor TLS authentication used */
+    ACSE_AUTH_NONE = 0,
+
+    /** Use ACSE password for client authentication */
+    ACSE_AUTH_PASSWORD = 1,
+
+    /** Use ACSE certificate for client authentication */
+    ACSE_AUTH_CERTIFICATE = 2,
+
+    /** Use TLS certificate for client authentication */
+    ACSE_AUTH_TLS = 3
+} AcseAuthenticationMechanism;
+
+
+typedef struct sAcseAuthenticationParameter* AcseAuthenticationParameter;
+
+struct sAcseAuthenticationParameter
+{
+    AcseAuthenticationMechanism mechanism;
+
+    union
+    {
+        struct
+        {
+            uint8_t* octetString;
+            int passwordLength;
+        } password; /* for mechanism = ACSE_AUTH_PASSWORD */
+
+        struct
+        {
+            uint8_t* buf;
+            int length;
+        } certificate; /* for mechanism = ACSE_AUTH_CERTIFICATE or ACSE_AUTH_TLS */
+
+    } value;
+};
+
+LIB61850_API AcseAuthenticationParameter
+AcseAuthenticationParameter_create(void);
+
+LIB61850_API void
+AcseAuthenticationParameter_destroy(AcseAuthenticationParameter self);
+
+LIB61850_API void
+AcseAuthenticationParameter_setAuthMechanism(AcseAuthenticationParameter self, AcseAuthenticationMechanism mechanism);
+
+LIB61850_API void
+AcseAuthenticationParameter_setPassword(AcseAuthenticationParameter self, char* password);
+
+
+/**
+ * \brief Callback function to authenticate a client
+ *
+ * \param parameter user provided parameter - set when user registers the authenticator
+ * \param authParameter the authentication parameters provided by the client
+ * \param securityToken pointer where to store an application specific security token - can be ignored if not used.
+ * \param appReference ISO application reference (ap-title + ae-qualifier)
+ *
+ * \return true if client connection is accepted, false otherwise
+ */
+typedef bool
+(*AcseAuthenticator)(void* parameter, AcseAuthenticationParameter authParameter, void** securityToken, IsoApplicationReference* appReference);
+
+/**
+ * \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 */
+} TSelector;
+
+/**
+ * \brief OSI session selector
+ *
+ * To not use S SEL set size to 0
+ */
+typedef struct {
+    uint8_t size; /** 0 .. 16 - 0 means S-selector is not present */
+    uint8_t value[16]; /** S-selector value */
+} SSelector;
+
+/**
+ * \brief OSI presentation (P) selector
+ *
+ * To not use P SEL set size to 0
+ */
+typedef struct {
+    uint8_t size; /** 0 .. 16 - 0 means P-selector is not present */
+    uint8_t value[16]; /** P-selector value */
+} PSelector;
+
+struct sIsoConnectionParameters
+{
+    AcseAuthenticationParameter acseAuthParameter;
+
+#if (CONFIG_MMS_SUPPORT_TLS == 1)
+    TLSConfiguration tlsConfiguration;
+#endif
+
+    const char* hostname;
+    int tcpPort;
+
+    const char* localIpAddress;
+    int localTcpPort;
+
+    uint8_t remoteApTitle[10];
+    int remoteApTitleLen;
+    int remoteAEQualifier;
+    PSelector remotePSelector;
+    SSelector remoteSSelector;
+    TSelector remoteTSelector;
+
+
+    uint8_t localApTitle[10];
+    int localApTitleLen;
+    int localAEQualifier;
+    PSelector localPSelector;
+    SSelector localSSelector;
+    TSelector localTSelector;
+
+};
+
+typedef struct sIsoConnectionParameters* IsoConnectionParameters;
+
+/**
+ * \brief create a new IsoConnectionParameters instance (FOR LIBRARY INTERNAL USE)
+ *
+ * NOTE: This function used internally by the MMS client library. When using the MMS or IEC 61850 API
+ * there should be no reason for the user to call this function.
+ *
+ * \return new IsoConnectionParameters instance
+ */
+LIB61850_API IsoConnectionParameters
+IsoConnectionParameters_create(void);
+
+/**
+ * \brief Destroy an IsoConnectionParameters instance (FOR LIBRARY INTERNAL USE)
+ *
+ * NOTE: This function used internally by the MMS client library. When using the MMS or IEC 61850 API
+ * there should be no reason for the user to call this function.
+ *
+ * \param self the IsoConnectionParameters instance
+ */
+LIB61850_API void
+IsoConnectionParameters_destroy(IsoConnectionParameters self);
+
+
+LIB61850_API void
+IsoConnectionParameters_setTlsConfiguration(IsoConnectionParameters self, TLSConfiguration tlsConfig);
+
+/**
+ * \brief set the authentication parameter
+ *
+ * This will set the authentication parameter and activates authentication.
+ *
+ * \param self the IsoConnectionParameters instance
+ * \param acseAuthParameter
+ */
+LIB61850_API void
+IsoConnectionParameters_setAcseAuthenticationParameter(IsoConnectionParameters self,
+        AcseAuthenticationParameter acseAuthParameter);
+
+/**
+ * \brief Set TCP parameters (FOR LIBRARY INTERNAL USE)
+ *
+ * NOTE: This function used internally by the MMS client library. When using the MMS or IEC 61850 API
+ * there should be no reason for the user to call this function
+ *
+ * \param self the IsoConnectionParameters instance
+ * \param hostname the hostname of IP address if the server
+ * \param tcpPort the TCP port number of the server
+ */
+LIB61850_API void
+IsoConnectionParameters_setTcpParameters(IsoConnectionParameters self, const char* hostname, int tcpPort);
+
+/**
+* \brief Set Local TCP parameters (FOR LIBRARY INTERNAL USE)
+*
+* NOTE: This function used internally by the MMS Client library. When using the MMS or IEC 61850 API
+* there should be no reason for the user to call this function
+*
+* \param self the IsoConnectionParameters instance
+* \param localIpAddress the hostname of local IP address of the server
+* \param localTcpPort the local TCP port number of the server
+*/
+LIB61850_API void
+IsoConnectionParameters_setLocalTcpParameters(IsoConnectionParameters self, const char* localIpAddress, int localTcpPort);
+
+/**
+ * \brief set the remote AP-Title and AE-Qualifier
+ *
+ * Calling this function is optional and not recommended. If not called the default
+ * parameters are used.
+ * If apTitle is NULL the parameter the AP-Title and AE-Qualifier will not be transmitted.
+ * This seems to be required by some server devices.
+ *
+ * \param self the IsoConnectionParameters instance
+ * \param apTitle the AP-Title OID as string.
+ * \param aeQualifier the AP-qualifier
+ */
+LIB61850_API void
+IsoConnectionParameters_setRemoteApTitle(IsoConnectionParameters self, const char* apTitle, int aeQualifier);
+
+/**
+ * \brief set remote addresses for the lower layers
+ *
+ * This function can be used to set the addresses for the lower layer protocols (presentation, session, and transport
+ * layer). Calling this function is optional and not recommended. If not called the default
+ * parameters are used.
+ *
+ *  \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 (ISO transport layer address)
+ */
+LIB61850_API void
+IsoConnectionParameters_setRemoteAddresses(IsoConnectionParameters self, PSelector pSelector, SSelector sSelector, TSelector tSelector);
+
+/**
+ * \brief set the local AP-Title and AE-Qualifier
+ *
+ * Calling this function is optional and not recommended. If not called the default
+ * parameters are used.
+ * If apTitle is NULL the parameter the AP-Title and AE-Qualifier will not be transmitted.
+ * This seems to be required by some server devices.
+ *
+ * \param self the IsoConnectionParameters instance
+ * \param apTitle the AP-Title OID as string.
+ * \param aeQualifier the AP-qualifier
+ */
+LIB61850_API void
+IsoConnectionParameters_setLocalApTitle(IsoConnectionParameters self, const char* apTitle, int aeQualifier);
+
+/**
+ * \brief set local addresses for the lower layers
+ *
+ * This function can be used to set the addresses for the lower layer protocols (presentation, session, and transport
+ * layer). Calling this function is optional and not recommended. If not called the default
+ * parameters are used.
+ *
+ *  \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 (ISO transport layer address)
+ */
+LIB61850_API void
+IsoConnectionParameters_setLocalAddresses(IsoConnectionParameters self, PSelector pSelector, SSelector sSelector, TSelector tSelector);
+
+/**@}*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ISO_CONNECTION_PARAMETERS_H_ */

+ 49 - 0
library/include/libiec61850/libiec61850_common_api.h

@@ -0,0 +1,49 @@
+/*
+ * libiec61850_common_api.h
+ */
+
+#ifndef LIBIEC61850_COMMON_API_INCLUDES_H_
+#define LIBIEC61850_COMMON_API_INCLUDES_H_
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+#ifdef __GNUC__
+#define ATTRIBUTE_PACKED __attribute__ ((__packed__))
+#else
+#define ATTRIBUTE_PACKED
+#endif
+
+#ifndef DEPRECATED
+#if defined(__GNUC__) || defined(__clang__)
+  #define DEPRECATED __attribute__((deprecated))
+#else
+  #define DEPRECATED
+#endif
+#endif
+
+#if defined _WIN32 || defined __CYGWIN__
+    #ifdef EXPORT_FUNCTIONS_FOR_DLL
+        #define LIB61850_API __declspec(dllexport)
+    #else
+        #define LIB61850_API
+    #endif
+
+    #define LIB61850_INTERNAL
+#else
+    #if __GNUC__ >= 4
+        #define LIB61850_API __attribute__ ((visibility ("default")))
+        #define LIB61850_INTERNAL  __attribute__ ((visibility ("hidden")))
+    #else
+        #define LIB61850_API
+        #define LIB61850_INTERNAL
+    #endif
+#endif
+
+#include "hal_time.h"
+#include "mms_value.h"
+
+#endif /* LIBIEC61850_COMMON_API_INCLUDES_H_ */

+ 193 - 0
library/include/libiec61850/linked_list.h

@@ -0,0 +1,193 @@
+/*
+ *  linked_list.h
+ *
+ *  Copyright 2013-2021 Michael Zillgith
+ *
+ *  This file is part of libIEC61850.
+ *
+ *  libIEC61850 is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  libIEC61850 is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with libIEC61850.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef LINKED_LIST_H_
+#define LINKED_LIST_H_
+
+#include "libiec61850_common_api.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \addtogroup common_api_group
+ */
+/**@{*/
+
+/**
+ * \defgroup LINKED_LIST LinkedList data type definition and handling functions
+ */
+/**@{*/
+
+
+struct sLinkedList {
+	void* data;
+	struct sLinkedList* next;
+};
+
+/**
+ * \brief Reference to a linked list or to a linked list element.
+ */
+typedef struct sLinkedList* LinkedList;
+
+/**
+ * \brief Create a new LinkedList object
+ *
+ * \return the newly created LinkedList instance
+ */
+LIB61850_API LinkedList
+LinkedList_create(void);
+
+/**
+ * \brief Delete a LinkedList object
+ *
+ * This function destroy the LinkedList object. It will free all data structures used by the LinkedList
+ * instance. It will call free for all elements of the linked list. This function should only be used if
+ * simple objects (like dynamically allocated strings) are stored in the linked list.
+ *
+ * \param self the LinkedList instance
+ */
+LIB61850_API void
+LinkedList_destroy(LinkedList self);
+
+
+typedef void (*LinkedListValueDeleteFunction) (void*);
+
+/**
+ * \brief Delete a LinkedList object
+ *
+ * This function destroy the LinkedList object. It will free all data structures used by the LinkedList
+ * instance. It will call a user provided function for each data element. This user provided function is
+ * responsible to properly free the data element.
+ *
+ * \param self the LinkedList instance
+ * \param valueDeleteFunction a function that is called for each data element of the LinkedList with the pointer
+ *         to the linked list data element.
+ */
+LIB61850_API void
+LinkedList_destroyDeep(LinkedList self, LinkedListValueDeleteFunction valueDeleteFunction);
+
+/**
+ * \brief Delete a LinkedList object without freeing the element data
+ *
+ * This function should be used statically allocated data objects are stored in the LinkedList instance.
+ * Other use cases would be if the data elements in the list should not be deleted.
+ *
+ * \param self the LinkedList instance
+ */
+LIB61850_API void
+LinkedList_destroyStatic(LinkedList self);
+
+/**
+ * \brief Add a new element to the list
+ *
+ * This function will add a new data element to the list. The new element will the last element in the
+ * list.
+ *
+ * \param self the LinkedList instance
+ * \param data data to append to the LinkedList instance
+ */
+LIB61850_API void
+LinkedList_add(LinkedList self, void* data);
+
+/**
+ * \brief Check if the specified data is contained in the list
+ *
+ * \param self the LinkedList instance
+ * \param data data to remove from the LinkedList instance
+ *
+ * \return true if data is part of the list, false otherwise
+ */
+LIB61850_API bool
+LinkedList_contains(LinkedList self, void* data);
+
+/**
+ * \brief Removed the specified element from the list
+ *
+ * \param self the LinkedList instance
+ * \param data data to remove from the LinkedList instance
+ *
+ *  \return true if data has been removed from the list, false otherwise
+ */
+LIB61850_API bool
+LinkedList_remove(LinkedList self, void* data);
+
+/**
+ * \brief Get the list element specified by index (starting with 0).
+ *
+ * \param self the LinkedList instance
+ * \param index index of the requested element.
+ */
+LIB61850_API LinkedList
+LinkedList_get(LinkedList self, int index);
+
+/**
+ * \brief Get the next element in the list (iterator).
+ *
+ * \param self the LinkedList instance
+ */
+LIB61850_API LinkedList
+LinkedList_getNext(LinkedList self);
+
+/**
+ * \brief Get the last element in the list.
+ *
+ * \param self the LinkedList instance
+ */
+LIB61850_API LinkedList
+LinkedList_getLastElement(LinkedList self);
+
+/**
+ * \brief Insert a new element int the list
+ *
+ * \param listElement the LinkedList instance
+ */
+LIB61850_API LinkedList
+LinkedList_insertAfter(LinkedList listElement, void* data);
+
+/**
+ * \brief Get the size of the list
+ *
+ * \param self the LinkedList instance
+ *
+ * \return number of data elements stored in the list
+ */
+LIB61850_API int
+LinkedList_size(LinkedList self);
+
+LIB61850_API void*
+LinkedList_getData(LinkedList self);
+
+LIB61850_API void
+LinkedList_printStringList(LinkedList self);
+
+/**@}*/
+
+/**@}*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LINKED_LIST_H_ */

+ 213 - 0
library/include/libiec61850/logging_api.h

@@ -0,0 +1,213 @@
+/*
+ *  logging_api.h
+ *
+ *  Copyright 2016 Michael Zillgith
+ *
+ *  This file is part of libIEC61850.
+ *
+ *  libIEC61850 is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  libIEC61850 is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with libIEC61850.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef LIBIEC61850_SRC_LOGGING_LOGGING_API_H_
+#define LIBIEC61850_SRC_LOGGING_LOGGING_API_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "libiec61850_common_api.h"
+
+
+/** \addtogroup server_api_group
+ *  @{
+ */
+
+/**
+ * @defgroup LOGGING_SPI Service provider interface (SPI) for log storage implementations
+ *
+ * This interface has to be implemented by the log storage provider. The Log storage provider
+ * has to provide a specific constructor that creates an instance of LogStorage by allocating
+ * the required memory for the struct sLogStorage data structure and populate the function
+ * pointers with provider specific implementation functions.
+ *
+ * @{
+ */
+
+/** The LogStorage object handle */
+typedef struct sLogStorage* LogStorage;
+
+/**
+ * \brief Will be called for each new LogEntry by the getEntries and getEntriesAfter functions
+ *
+ * \param parameter - a user provided parameter that is passed to the callback handler
+ * \param timestamp - the entry timestamp of the LogEntry
+ * \param entryID - the entryID of the LogEntry
+ * \param moreFollow - more data will follow - if false, the data is not valid and no more data will follow
+ *
+ * \return true ready to receive new data, false abort operation
+ */
+typedef bool (*LogEntryCallback) (void* parameter, uint64_t timestamp, uint64_t entryID, bool moreFollow);
+
+/**
+ * \brief Will be called for each new LogEntryData by the getEntries and getEntriesAfter functions
+ *
+ * \param parameter - a user provided parameter that is passed to the callback handler
+ * \param dataRef - the data reference of the LogEntryData
+ * \param data - the data content as an unstructured binary data block
+ * \param dataSize - the size of the binary data block
+ * \param reasonCode - the reasonCode of the LogEntryData
+ * \param moreFollow - more data will follow - if false, the data is not valid and no more data will follow
+ *
+ * \return true ready to receive new data, false abort operation
+ */
+typedef bool (*LogEntryDataCallback) (void* parameter, const char* dataRef, uint8_t* data, int dataSize, uint8_t reasonCode, bool moreFollow);
+
+struct sLogStorage {
+
+    void* instanceData;
+
+    int maxLogEntries;
+
+    uint64_t (*addEntry) (LogStorage self, uint64_t timestamp);
+
+    bool (*addEntryData) (LogStorage self, uint64_t entryID, const char* dataRef, uint8_t* data, int dataSize, uint8_t reasonCode);
+
+    bool (*getEntries) (LogStorage self, uint64_t startingTime, uint64_t endingTime,
+            LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* parameter);
+
+    bool (*getEntriesAfter) (LogStorage self, uint64_t startingTime, uint64_t entryID,
+            LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* parameter);
+
+    bool (*getOldestAndNewestEntries) (LogStorage self, uint64_t* newEntry, uint64_t* newEntryTime,
+            uint64_t* oldEntry, uint64_t* oldEntryTime);
+
+    void (*destroy) (LogStorage self);
+};
+
+
+/**
+ * \brief Set the maximum number of log entries for this log
+ *
+ * \param self the pointer of the LogStorage instance
+ * \param maxEntries the maximum number of log entries
+ */
+LIB61850_API void
+LogStorage_setMaxLogEntries(LogStorage self, int maxEntries);
+
+/**
+ * \brief Get the maximum allowed number of log entries for this log
+ *
+ * \param self the pointer of the LogStorage instance
+ *
+ * \return the maximum number of log entries
+ */
+LIB61850_API int
+LogStorage_getMaxLogEntries(LogStorage self);
+
+/**
+ * \brief Add an entry to the log
+ *
+ * \param self the pointer of the LogStorage instance
+ * \param timestamp the entry time of the new entry
+ *
+ * \return the entryID of the new entry
+ */
+LIB61850_API uint64_t
+LogStorage_addEntry(LogStorage self, uint64_t timestamp);
+
+/**
+ * \brief Add new entry data to an existing log entry
+ *
+ * \param self the pointer of the LogStorage instance
+ * \param entryID the ID of the log entry where the data will be added
+ * \param dataRef the data reference of the log entry data
+ * \param data the data content as an unstructured binary data block
+ * \param dataSize - the size of the binary data block
+ * \param reasonCode - the reasonCode of the LogEntryData
+ *
+ * \return true if the entry data was successfully added, false otherwise
+ */
+LIB61850_API bool
+LogStorage_addEntryData(LogStorage self, uint64_t entryID, const char* dataRef, uint8_t* data, int dataSize, uint8_t reasonCode);
+
+/**
+ * \brief Get log entries specified by a time range
+ *
+ * \param self the pointer of the LogStorage instance
+ * \param startingTime start time of the time range
+ * \param endingTime end time of the time range
+ * \param entryCallback callback function to be called for each new log entry
+ * \param entryDataCallback callback function to be called for each new log entry data
+ * \param parameter - a user provided parameter that is passed to the callback handler
+ *
+ * \return true if the request has been successful, false otherwise
+ */
+LIB61850_API bool
+LogStorage_getEntries(LogStorage self, uint64_t startingTime, uint64_t endingTime,
+        LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* parameter);
+
+/**
+ * \brief Get log entries specified by a start log entry
+ *
+ * The request will return all log entries that where entered into the log after the
+ * log entry specified by startingTime and entryID.
+ *
+ * \param self the pointer of the LogStorage instance
+ * \param startingTime timestamp of the start log entry
+ * \param entryID entryID of the start log entry
+ * \param entryCallback callback function to be called for each new log entry
+ * \param entryDataCallback callback function to be called for each new log entry data
+ * \param parameter - a user provided parameter that is passed to the callback handler
+ *
+ * \return true if the request has been successful, false otherwise
+ */
+LIB61850_API bool
+LogStorage_getEntriesAfter(LogStorage self, uint64_t startingTime, uint64_t entryID,
+        LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* parameter);
+
+/**
+ * \brief Get the entry time and entryID of the oldest and the newest log entries
+ *
+ * This function is used to update the log status information in the LCB
+ *
+ * \param self the pointer of the LogStorage instance
+ * \param newEntry pointer to store the entryID of the newest entry
+ * \param newEntryTime pointer to store the entry time of the newest entry
+ * \param oldEntry pointer to store the entryID of the oldest entry
+ * \param oldEntryTime pointer to store the entry time of the oldest entry
+ *
+ */
+LIB61850_API bool
+LogStorage_getOldestAndNewestEntries(LogStorage self, uint64_t* newEntry, uint64_t* newEntryTime,
+        uint64_t* oldEntry, uint64_t* oldEntryTime);
+
+/**
+ * \brief Destroy the LogStorage instance and free all related resources
+ *
+ * \param self the pointer of the LogStorage instance
+ */
+LIB61850_API void
+LogStorage_destroy(LogStorage self);
+
+/**@}*/
+
+/**@}*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBIEC61850_SRC_LOGGING_LOGGING_API_H_ */

+ 1333 - 0
library/include/libiec61850/mms_client_connection.h

@@ -0,0 +1,1333 @@
+/*
+ *  mms_client_connection.h
+ *
+ *  Copyright 2013-2018 Michael Zillgith
+ *
+ *  This file is part of libIEC61850.
+ *
+ *  libIEC61850 is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  libIEC61850 is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with libIEC61850.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef MMS_CLIENT_CONNECTION_H_
+#define MMS_CLIENT_CONNECTION_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \defgroup mms_client_api_group MMS client API (for IEC 61850 use IEC 61850 client API instead!)
+ */
+/**@{*/
+
+#include "libiec61850_common_api.h"
+
+#include "mms_common.h"
+#include "mms_type_spec.h"
+#include "mms_value.h"
+#include "iso_connection_parameters.h"
+#include "linked_list.h"
+#include "tls_config.h"
+
+/**
+ * Contains MMS layer specific parameters
+ */
+typedef struct sMmsConnectionParameters {
+	int maxServOutstandingCalling;
+	int maxServOutstandingCalled;
+	int dataStructureNestingLevel;
+	int maxPduSize; /* local detail */
+	uint8_t servicesSupported[11];
+} MmsConnectionParameters;
+
+typedef struct {
+    char* vendorName;
+    char* modelName;
+    char* revision;
+} MmsServerIdentity;
+
+typedef enum {
+    MMS_CONNECTION_STATE_CLOSED,
+    MMS_CONNECTION_STATE_CONNECTING,
+    MMS_CONNECTION_STATE_CONNECTED,
+    MMS_CONNECTION_STATE_CLOSING
+} MmsConnectionState;
+
+typedef void (*MmsInformationReportHandler) (void* parameter, char* domainName,
+        char* variableListName, MmsValue* value, bool isVariableListName);
+
+/**
+ * Opaque handle for MMS client connection instance.
+ */
+typedef struct sMmsConnection* MmsConnection;
+
+
+/*******************************************************************************
+ * Connection management functions
+ *******************************************************************************/
+
+/**
+ * \brief Create a new MmsConnection instance
+ *
+ * \return the newly created instance.
+ */
+LIB61850_API MmsConnection
+MmsConnection_create(void);
+
+/**
+ * \brief Create a new secure (TLS enabled) MmsConnection instance
+ *
+ * \param tlsConfig TLS configuration parameters and certificates
+ *
+ * \return the newly created instance.
+ */
+LIB61850_API MmsConnection
+MmsConnection_createSecure(TLSConfiguration tlsConfig);
+
+/**
+ * \brief Create a new MmsConnection instance configured for non-threaded mode
+ *
+ * NOTE: This constructor doesn't create a background thread for connection handling.
+ * The user has to call the MmsConnection_tick function periodically to ensure that
+ * the MMS connection can be handled properly.
+ *
+ * \param tlsConfig TLS configuration parameters and certificates or NULL for non-TLS mode.
+ *
+ * \return  the newly created instance.
+ */
+LIB61850_API MmsConnection
+MmsConnection_createNonThreaded(TLSConfiguration tlsConfig);
+
+/**
+ * \brief Callback function to intercept raw MMS messages
+ *
+ * IMPORTANT: the message buffer is only valid in the context of the the callback function. If the
+ * message data is required elsewhere it has to be copied here!
+ *
+ * \param parameter user provided parameter that is passed to the callback function
+ * \param message buffer of the message.
+ * \param messageLength length of the message in bytes
+ * \param received if true message has been received, false when message has been sent.
+ */
+typedef void (*MmsRawMessageHandler) (void* parameter, uint8_t* message, int messageLength, bool received);
+
+/**
+ * \brief Set the callback handler to intercept the raw MMS messages of the connection
+ *
+ * This function can be used to log raw MMS messages. It may be useful for debugging purposes
+ * or advanced test tools. This function will only work when the flag CONFIG_MMS_RAW_MESSAGE_LOGGING
+ * it set in stack_config.h
+ *
+ * \param self MmsConnection instance to operate on
+ * \param handler the connection specific callback function
+ * \param a user provided parameter passed to the callback function (use NULL if not required).
+ */
+LIB61850_API void
+MmsConnection_setRawMessageHandler(MmsConnection self, MmsRawMessageHandler handler, void* parameter);
+
+/**
+ * \brief Set the virtual filestore basepath for the MMS obtain file services
+ *
+ * All external file service accesses will be mapped to paths relative to the base directory.
+ * NOTE: This function is only available when the CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME
+ * option in stack_config.h is set.
+ *
+ * \param self the MmsServer instance
+ * \param basepath the new virtual filestore basepath
+ */
+LIB61850_API void
+MmsConnection_setFilestoreBasepath(MmsConnection self, const char* basepath);
+
+/**
+ * \brief Set the request timeout in ms for this connection
+ *
+ * \param self MmsConnection instance to operate on
+ * \param timeoutInMs request timeout in milliseconds
+ */
+LIB61850_API void
+MmsConnection_setRequestTimeout(MmsConnection self, uint32_t timeoutInMs);
+
+/**
+ * \brief Set the maximum number outstanding calls allowed for this connection
+ *
+ * \param self MmsConnection instance to operate on
+ * \param calling the maximum outstanding calls allowed by the caller (client)
+ * \param called the maximum outstanding calls allowed by the called endpoint (server)
+ */
+LIB61850_API void
+MmsConnnection_setMaxOutstandingCalls(MmsConnection self, int calling, int called);
+
+/**
+ * \brief Get the request timeout in ms for this connection
+ *
+ * \param self MmsConnection instance to operate on
+ *
+ * \return request timeout in milliseconds
+ */
+LIB61850_API uint32_t
+MmsConnection_getRequestTimeout(MmsConnection self);
+
+/**
+ * \brief Set the connect timeout in ms for this connection instance
+ *
+ * \param self MmsConnection instance to operate on
+ * \param timeoutInMs connect timeout in milliseconds
+ */
+LIB61850_API void
+MmsConnection_setConnectTimeout(MmsConnection self, uint32_t timeoutInMs);
+
+/**
+ * \brief Install a handler function for MMS information reports (unsolicited messages from the server).
+ *
+ * The handler function will be called whenever the client receives an MMS information report message.
+ * Note that the API user is responsible to properly free the passed MmsValue object.
+ *
+ * \param self MmsConnection instance to operate on
+ * \param handler the handler function to install for this client connection
+ * \param parameter a user specified parameter that will be passed to the handler function on each
+ *        invocation.
+ */
+LIB61850_API void
+MmsConnection_setInformationReportHandler(MmsConnection self, MmsInformationReportHandler handler,
+        void* parameter);
+
+/**
+ * \brief Get the ISO connection parameters for an MmsConnection instance
+ *
+ * \param self MmsConnection instance to operate on
+ * \return params the to be used by this connection
+ */
+LIB61850_API IsoConnectionParameters
+MmsConnection_getIsoConnectionParameters(MmsConnection self);
+
+/**
+ * \brief Get the MMS specific connection parameters for an MmsConnection instance
+ *
+ * \param self MmsConnection instance to operate on
+ * \return params the to be used by this connection
+ */
+LIB61850_API MmsConnectionParameters
+MmsConnection_getMmsConnectionParameters(MmsConnection self);
+
+typedef void (*MmsConnectionStateChangedHandler) (MmsConnection connection, void* parameter, MmsConnectionState newState);
+
+LIB61850_API void
+MmsConnection_setConnectionStateChangedHandler(MmsConnection self, MmsConnectionStateChangedHandler handler, void* parameter);
+
+/**
+ * \brief User provided handler function that will be called if the connection to the server is lost
+ *
+ * \param connection MmsConnection object of the lost connection
+ * \param parameter user provided parameter.
+ */
+typedef void (*MmsConnectionLostHandler) (MmsConnection connection, void* parameter);
+
+/**
+ * \brief Install a callback function that will be called by the client stack if the MMS connection to the server is lost
+ *
+ * \param handler the user provided callback function
+ * \param handlerParameter a parameter that will be passed to the callback function. Can be set to NULL if not required.
+ */
+LIB61850_API void
+MmsConnection_setConnectionLostHandler(MmsConnection self, MmsConnectionLostHandler handler, void* handlerParameter);
+
+/**
+ * \brief Set the ISO connection parameters for a MmsConnection instance
+ *
+ * \param self MmsConnection instance to operate on
+ * \param params the ISO client parameters to use
+ */
+LIB61850_API void
+MmsConnection_setIsoConnectionParameters(MmsConnection self, IsoConnectionParameters* params);
+
+/**
+ * \brief Destroy an MmsConnection instance and release all resources
+ *
+ * \param self MmsConnection instance to operate on
+ */
+LIB61850_API void
+MmsConnection_destroy(MmsConnection self);
+
+/*******************************************************************************
+ * Blocking functions for connection establishment and data access
+ *******************************************************************************/
+
+
+/**
+ * \brief Connect to an MMS server.
+ *
+ * This will open a new TCP connection and send a MMS initiate request.
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param serverName hostname or IP address of the server to connect
+ * \param serverPort TCP port number of the server to connect or -1 to use default port (102 for MMS or 3872 for MMS over TLS)
+ *
+ * \return true on success. false if the connection attempt failed.
+ */
+LIB61850_API bool
+MmsConnection_connect(MmsConnection self, MmsError* mmsError, const char* serverName, int serverPort);
+
+
+
+LIB61850_API void
+MmsConnection_connectAsync(MmsConnection self, MmsError* mmsError, const char* serverName, int serverPort);
+
+/**
+ * \brief Call MmsConnection state machine and connection handling code (for non-threaded mode only)
+ *
+ * This function has to be called periodically by the user application in non-threaded mode.
+ *
+ * \return true when connection is currently waiting and calling thread can be suspended, false means
+ *         connection is busy and the tick function should be called again as soon as possible.
+ */
+LIB61850_API bool
+MmsConnection_tick(MmsConnection self);
+
+/* NOTE: This function is for test purposes! */
+LIB61850_API void
+MmsConnection_sendRawData(MmsConnection self, MmsError* mmsError, uint8_t* buffer, int bufSize);
+
+/**
+ * \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
+ */
+LIB61850_API void
+MmsConnection_close(MmsConnection self);
+
+typedef void
+(*MmsConnection_ConcludeAbortHandler) (void* parameter, MmsError mmsError, bool success);
+
+/**
+ * \brief Uses the MMS/ACSE abort service to close the connection to the server
+ *
+ * This service should be used to abruptly interrupt the connection to the server. It is not quite clear what the
+ * benefit of this service is (simply closing the TCP connection should do the same). Though it is required by
+ * conformance tests. In case the server doesn't close the connection after the internal timeout interval the
+ * client will close the TCP connection and set mmsError to MMS_ERROR_SERVICE_TIMEOUT.
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ */
+LIB61850_API void
+MmsConnection_abort(MmsConnection self, MmsError* mmsError);
+
+LIB61850_API void
+MmsConnection_abortAsync(MmsConnection self, MmsError* mmsError);
+
+/**
+ * \brief Uses the MMS conclude service to close the connection to the server
+ *
+ * This should be used to orderly release the connection to the server. If the server denies the conclude
+ * request (by sending a concludeError PDU) this service fails with an error (mmsError set accordingly) and
+ * the connection remains open. In this case the close or abort methods have to be used to close the connection.
+ * It is not quite clear if this service is really useful but it is required by conformance tests.
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ */
+LIB61850_API void
+MmsConnection_conclude(MmsConnection self, MmsError* mmsError);
+
+LIB61850_API void
+MmsConnection_concludeAsync(MmsConnection self, MmsError* mmsError, MmsConnection_ConcludeAbortHandler handler, void* parameter);
+
+typedef void
+(*MmsConnection_GenericServiceHandler) (uint32_t invokeId, void* parameter, MmsError mmsError, bool success);
+
+typedef void
+(*MmsConnection_GetNameListHandler) (uint32_t invokeId, void* parameter, MmsError mmsError, LinkedList nameList, bool moreFollows);
+
+/**
+ * \brief Get the names of all VMD scope variables of the server.
+ *
+ * This will result in a VMD specific GetNameList request.
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ *
+ * \return the of VMD specific variable names or NULL if the request failed.
+ */
+LIB61850_API LinkedList /* <char*> */
+MmsConnection_getVMDVariableNames(MmsConnection self, MmsError* mmsError);
+
+LIB61850_API void
+MmsConnection_getVMDVariableNamesAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* continueAfter,
+        MmsConnection_GetNameListHandler handler, void* parameter);
+
+/**
+ * \brief Get the domains names for all domains of the server.
+ *
+ * This will result in a VMD specific GetNameList request.
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variaextern "C" {ble to store error code
+ *
+ * \return the list of domain names or NULL if the request failed.
+ *
+ */
+LIB61850_API LinkedList /* <char*> */
+MmsConnection_getDomainNames(MmsConnection self, MmsError* mmsError);
+
+/**
+ * \brief Get the domain names of the server (asynchronous version).
+ *
+ * \param[in] self MmsConnection instance to operate on
+ * \param[out] usedInvokeId the invoke ID of the request
+ * \param[out] mmsError user provided variable to store error code
+ * \param[in] continueAfter the name of the last received element when the call is a continuation, or NULL for the first call
+ * \param[in] result list to store (append) the response names, or NULL to create a new list for the response names
+ * \param[in] handler will be called when response is received or timed out.
+ * \param[in] parameter
+ */
+LIB61850_API void
+MmsConnection_getDomainNamesAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* continueAfter, LinkedList result,
+        MmsConnection_GetNameListHandler handler, void* parameter);
+
+/**
+ * \brief Get the names of all variables present in a MMS domain of the server.
+ *
+ * This will result in a domain specific GetNameList request.
+ *
+ * \param[in] self MmsConnection instance to operate on
+ * \param[out] mmsError user provided variable to store error code
+ * \param[in] domainId the domain name for the domain specific request
+ *
+ * \return the of domain specific variable names or NULL if the request failed.
+ */
+LIB61850_API LinkedList /* <char*> */
+MmsConnection_getDomainVariableNames(MmsConnection self, MmsError* mmsError, const char* domainId);
+
+/**
+ * \brief Get the names of all variables present in a MMS domain of the server (asynchronous version).
+ *
+ * This will result in a domain specific GetNameList request.
+ *
+ * \param[in] self MmsConnection instance to operate on
+ * \param[out] usedInvokeId the invoke ID of the request
+ * \param[out] mmsError user provided variable to store error code
+ * \param[in] domainId the domain name for the domain specific request
+ * \param[in] continueAfter the name of the last received element when the call is a continuation, or NULL for the first call
+ * \param[in] result list to store (append) the response names, or NULL to create a new list for the response names
+ * \param[in] handler will be called when response is received or timed out.
+ * \param[in] parameter
+ */
+LIB61850_API void
+MmsConnection_getDomainVariableNamesAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId,
+        const char* continueAfter, LinkedList result, MmsConnection_GetNameListHandler handler, void* parameter);
+
+/**
+ * \brief Get the names of all named variable lists present in a MMS domain or VMD scope of the server.
+ *
+ * This will result in a domain specific GetNameList request.
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param domainId the domain name for the domain specific request or NULL for a VMD scope request
+ *
+ * \return the domain specific named variable list names or NULL if the request failed.
+ */
+LIB61850_API LinkedList /* <char*> */
+MmsConnection_getDomainVariableListNames(MmsConnection self, MmsError* mmsError, const char* domainId);
+
+LIB61850_API void
+MmsConnection_getDomainVariableListNamesAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId,
+        const char* continueAfter, LinkedList result, MmsConnection_GetNameListHandler handler, void* parameter);
+
+/**
+ * \brief Get the names of all journals present in a MMS domain of the server
+ *
+ * This will result in a domain specific GetNameList request.
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param domainId the domain name for the domain specific request
+ *
+ * \return the domain specific journal names or NULL if the request failed.
+ */
+LIB61850_API LinkedList /* <char*> */
+MmsConnection_getDomainJournals(MmsConnection self, MmsError* mmsError, const char* domainId);
+
+LIB61850_API void
+MmsConnection_getDomainJournalsAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId,
+        const char* continueAfter, MmsConnection_GetNameListHandler handler, void* parameter);
+
+/**
+ * \brief Get the names of all named variable lists associated with this client connection.
+ *
+ * This will result in an association specific GetNameList request.
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ *
+ * \return the association specific named variable list names or NULL if the request failed.
+ */
+LIB61850_API LinkedList /* <char*> */
+MmsConnection_getVariableListNamesAssociationSpecific(MmsConnection self, MmsError* mmsError);
+
+LIB61850_API void
+MmsConnection_getVariableListNamesAssociationSpecificAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
+        const char* continueAfter, MmsConnection_GetNameListHandler handler, void* parameter);
+
+
+/**
+ * \brief Read a single variable from the server.
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param domainId the domain name of the variable to be read or NULL to read a VMD specific named variable
+ * \param itemId name of the variable to be read
+ *
+ * \return Returns a MmsValue object or NULL if the request failed. The MmsValue object can
+ * either be a simple value or a complex value or array. It is also possible that the return value is NULL
+ * even if mmsError = MMS_ERROR_NON. This is the case when the servers returns an empty result list.
+ */
+LIB61850_API MmsValue*
+MmsConnection_readVariable(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId);
+
+
+typedef void
+(*MmsConnection_ReadVariableHandler) (uint32_t invokeId, void* parameter, MmsError mmsError, MmsValue* value);
+
+/**
+ * \brief Read a single variable from the server (asynchronous version)
+ *
+ * \param{in] self MmsConnection instance to operate on
+ * \param[out] usedInvokeId the invoke ID of the request
+ * \param[out] mmsError user provided variable to store error code
+ * \param[in] domainId the domain name of the variable to be read or NULL to read a VMD specific named variable
+ * \param[in] itemId name of the variable to be read
+ */
+LIB61850_API void
+MmsConnection_readVariableAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId, const char* itemId,
+        MmsConnection_ReadVariableHandler handler, void* parameter);
+
+/**
+ * \brief Read a component of a single variable from the server.
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param domainId the domain name of the variable to be read or NULL to read a VMD specific named variable
+ * \param itemId name of the variable to be read
+ * \param componentId the component name
+ *
+ * \return Returns a MmsValue object or NULL if the request failed. The MmsValue object can
+ * either be a simple value or a complex value or array. It is also possible that the return value is NULL
+ * even if mmsError = MMS_ERROR_NON. This is the case when the servers returns an empty result list.
+ */
+LIB61850_API MmsValue*
+MmsConnection_readVariableComponent(MmsConnection self, MmsError* mmsError,
+        const char* domainId, const char* itemId, const char* componentId);
+
+/**
+ * \brief Read a component of a single variable from the server (asynchronous version)
+ *
+ * \param[in] self MmsConnection instance to operate on
+ * \param[out] usedInvokeId the invoke ID of the request
+ * \param[out] mmsError user provided variable to store error code
+ * \param[in] domainId the domain name of the variable to be read or NULL to read a VMD specific named variable
+ * \param[in] itemId name of the variable to be read
+ * \param[in] componentId the component name
+ * \param[in] handler
+ * \param[in] parameter
+ */
+LIB61850_API void
+MmsConnection_readVariableComponentAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
+        const char* domainId, const char* itemId, const char* componentId,
+        MmsConnection_ReadVariableHandler handler, void* parameter);
+
+/**
+ * \brief Read one or more elements of a single array variable from the server.
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param domainId the domain name of the variable to be read
+ * \param itemId name of the variable to be read
+ * \param startIndex index of element to read or start index if a element range is to be read
+ * \param numberOfElements Number of elements to read or 0 if a single element is to be read
+ *
+ * \return Returns a MmsValue object or NULL if the request failed. The MmsValue object is either
+ * a simple or complex type if numberOfElements is 0, or an array containing the selected
+ * array elements of numberOfElements > 0.
+ */
+LIB61850_API MmsValue*
+MmsConnection_readArrayElements(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId,
+		uint32_t startIndex, uint32_t numberOfElements);
+
+/**
+ * \brief Read one or more elements of a single array variable from the server (asynchronous version)
+ *
+ * NOTE: The MmsValue object received by the callback function is either a simple or complex type if numberOfElements is 0, or an array
+ * containing the selected array elements of numberOfElements > 0.
+ *
+ * \param[in] self MmsConnection instance to operate on
+ * \param[out] usedInvokeId the invoke ID of the request
+ * \param[out] mmsError user provided variable to store error code
+ * \param[in] domainId the domain name of the variable to be read
+ * \param[in] itemId name of the variable to be read
+ * \param[in] startIndex index of element to read or start index if a element range is to be read
+ * \param[in] numberOfElements Number of elements to read or 0 if a single element is to be read
+ */
+LIB61850_API void
+MmsConnection_readArrayElementsAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId, const char* itemId,
+        uint32_t startIndex, uint32_t numberOfElements,
+        MmsConnection_ReadVariableHandler handler, void* parameter);
+
+
+/**
+ * \brief Read a single element (with optional component specification) from the server
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param domainId the domain name of the variable to be read
+ * \param itemId name of the variable to be read
+ * \param index array element index
+ * \param componentId array element component name
+ *
+ * \return Returns a MmsValue object or NULL if the request failed.
+ */
+LIB61850_API MmsValue*
+MmsConnection_readSingleArrayElementWithComponent(MmsConnection self, MmsError* mmsError,
+        const char* domainId, const char* itemId, uint32_t index, const char* componentId);
+
+LIB61850_API void
+MmsConnection_readSingleArrayElementWithComponentAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
+        const char* domainId, const char* itemId,
+        uint32_t index, const char* componentId,
+        MmsConnection_ReadVariableHandler handler, void* parameter);
+
+/**
+ * \brief Read multiple variables of a domain from the server with one request message.
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param domainId the domain name of the requested variables.
+ * \param items: LinkedList<char*> is the list of item IDs of the requested variables.
+ *
+ * \return  Returns a MmsValue object or NULL if the request failed. The MmsValue object is
+ * is of type MMS_ARRAY and contains the variable values of simple or complex type
+ * in the order as they appeared in the item ID list.
+ */
+LIB61850_API MmsValue*
+MmsConnection_readMultipleVariables(MmsConnection self, MmsError* mmsError, const char* domainId,
+		LinkedList /*<char*>*/ items);
+
+LIB61850_API void
+MmsConnection_readMultipleVariablesAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
+        const char* domainId, LinkedList /*<char*>*/items,
+        MmsConnection_ReadVariableHandler handler, void* parameter);
+
+/**
+ * \brief Write a single variable to the server.
+ *
+ * NOTE: added return value in version 1.1
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param domainId the domain name of the variable to be written
+ * \param itemId name of the variable to be written
+ * \param value value of the variable to be written
+ *
+ * \return when successful, the data access error value returned by the server
+ */
+LIB61850_API MmsDataAccessError
+MmsConnection_writeVariable(MmsConnection self, MmsError* mmsError,
+        const char* domainId, const char* itemId, MmsValue* value);
+
+typedef void
+(*MmsConnection_WriteVariableHandler) (uint32_t invokeId, void* parameter, MmsError mmsError, MmsDataAccessError accessError);
+
+LIB61850_API void
+MmsConnection_writeVariableAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
+        const char* domainId, const char* itemId, MmsValue* value,
+        MmsConnection_WriteVariableHandler handler, void* parameter);
+
+
+/**
+ * \brief Write a single variable to the server (using component alternate access)
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param domainId the domain name of the variable to be written
+ * \param itemId name of the variable to be written
+ * \param componentId the name of the variable component
+ * \param value value of the variable to be written
+ *
+ * \return when successful, the data access error value returned by the server
+ */
+LIB61850_API MmsDataAccessError
+MmsConnection_writeVariableComponent(MmsConnection self, MmsError* mmsError,
+        const char* domainId, const char* itemId,
+        const char* componentId, MmsValue* value);
+
+/**
+ * \brief Write a single array element with a component to an array type variable
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param domainId the domain name of the variable to be written
+ * \param itemId name of the variable to be written
+ * \param arrayIndex the index of the array element.
+ * \param componentId the name of the component of the array element
+ * \param value value of the array element component to be written.
+ *
+ * \return when successful, the data access error value returned by the server
+ */
+LIB61850_API MmsDataAccessError
+MmsConnection_writeSingleArrayElementWithComponent(MmsConnection self, MmsError* mmsError,
+        const char* domainId, const char* itemId,
+        uint32_t arrayIndex, const char* componentId, MmsValue* value);
+
+LIB61850_API void
+MmsConnection_writeSingleArrayElementWithComponentAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
+        const char* domainId, const char* itemId,
+        uint32_t arrayIndex, const char* componentId, MmsValue* value,
+        MmsConnection_WriteVariableHandler handler, void* parameter);
+
+LIB61850_API void
+MmsConnection_writeVariableComponentAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
+        const char* domainId, const char* itemId, const char* componentId, MmsValue* value,
+        MmsConnection_WriteVariableHandler handler, void* parameter);
+
+/**
+ * \brief Write a single array element or a sub array to an array type variable
+ *
+ *  When a single array element is addressed the MmsValue object value has to be of the type
+ *  of the array elements. When multiple array elements have to be written (index range) the
+ *  MmsValue object value has to be of type MMS_ARRAY containing "numberOfElements" elements.
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param domainId the domain name of the variable to be written
+ * \param index the index of the array element or the start index of a index range
+ * \param numberOfElements the number of array elements to write starting with index. If 0 only one array element is written.
+ * \param itemId name of the variable to be written
+ * \param value value of the array element(s) to be written. Has to be of the type of
+ *        the array elements or of type MMS_ARRAY when it is a sub array (index range)
+ *
+ * \return when successful, the data access error value returned by the server
+ */
+LIB61850_API MmsDataAccessError
+MmsConnection_writeArrayElements(MmsConnection self, MmsError* mmsError,
+        const char* domainId, const char* itemId, int index, int numberOfElements,
+        MmsValue* value);
+
+LIB61850_API void
+MmsConnection_writeArrayElementsAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
+        const char* domainId, const char* itemId, int index, int numberOfElements,
+        MmsValue* value,
+        MmsConnection_WriteVariableHandler handler, void* parameter);
+
+
+typedef void
+(*MmsConnection_WriteMultipleVariablesHandler) (uint32_t invokeId, void* parameter, MmsError mmsError, LinkedList /* <MmsValue*> */ accessResults);
+
+
+/**
+ * \brief Write multiple variables to the server.
+ *
+ * This function will write multiple variables to the server.
+ *
+ * The parameter accessResults is a pointer to a LinkedList reference. The methods will create a new LinkedList
+ * object that contains the AccessResults of the single variable write attempts. It is up to the user to free this
+ * objects properly (e.g. with LinkedList_destroyDeep(accessResults, MmsValue_delete)).
+ *
+ * \param[in] self MmsConnection instance to operate on
+ * \param[out] mmsError user provided variable to store error code
+ * \param[in] domainId the common domain name of all variables to be written
+ * \param[in] items a linked list containing the names of the variables to be written. The names are C strings.
+ * \param[out] values values of the variables to be written
+ * \param[out] the MmsValue objects of type MMS_DATA_ACCESS_ERROR representing the write success of a single variable
+ *        write.
+ */
+LIB61850_API void
+MmsConnection_writeMultipleVariables(MmsConnection self, MmsError* mmsError, const char* domainId,
+        LinkedList /*<char*>*/ items, LinkedList /* <MmsValue*> */ values,
+        LinkedList* /* <MmsValue*> */ accessResults);
+
+LIB61850_API void
+MmsConnection_writeMultipleVariablesAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId,
+        LinkedList /*<char*>*/ items, LinkedList /* <MmsValue*> */ values,
+        MmsConnection_WriteMultipleVariablesHandler handler, void* parameter);
+
+/**
+ * \brief Write named variable list values to the server.
+ *
+ * The parameter accessResults is a pointer to a LinkedList reference. The methods will create a new LinkedList
+ * object that contains the AccessResults of the single variable write attempts. It is in the responsibility of
+ * the user to free this objects properly (e.g. with LinkedList_destroyDeep(accessResults, MmsValue_delete)).
+ * If accessResult is the to NULL the result will not be stored.
+ *
+ * \param[in] self MmsConnection instance to operate on
+ * \param[out] mmsError user provided variable to store error code
+ * \param[in] isAssociationSpecifc true if the named variable list is an association specific named variable list
+ * \param[in] domainId the common domain name of all variables to be written
+ * \param[out] values values of the variables to be written
+ * \param[out] the MmsValue objects of type MMS_DATA_ACCESS_ERROR representing the write success of a single variable
+ *        write.
+ */
+LIB61850_API void
+MmsConnection_writeNamedVariableList(MmsConnection self, MmsError* mmsError, bool isAssociationSpecific,
+        const char* domainId, const char* itemId, LinkedList /* <MmsValue*> */values,
+        LinkedList* /* <MmsValue*> */accessResults);
+
+
+LIB61850_API void
+MmsConnection_writeNamedVariableListAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, bool isAssociationSpecific,
+        const char* domainId, const char* itemId, LinkedList /* <MmsValue*> */values,
+        MmsConnection_WriteMultipleVariablesHandler handler, void* parameter);
+
+/**
+ * \brief Get the variable access attributes of a MMS named variable of the server
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param domainId the domain name of the variable or NULL for a VMD specific request
+ * \param itemId name of the variable
+ *
+ * \return Returns a MmsTypeSpecification object or NULL if the request failed.
+ */
+LIB61850_API MmsVariableSpecification*
+MmsConnection_getVariableAccessAttributes(MmsConnection self, MmsError* mmsError,
+        const char* domainId, const char* itemId);
+
+typedef void
+(*MmsConnection_GetVariableAccessAttributesHandler) (uint32_t invokeId, void* parameter, MmsError mmsError, MmsVariableSpecification* spec);
+
+
+LIB61850_API void
+MmsConnection_getVariableAccessAttributesAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
+        const char* domainId, const char* itemId,
+        MmsConnection_GetVariableAccessAttributesHandler, void* parameter);
+
+/**
+ * \brief Read the values of a domain specific named variable list
+ *
+ * The resulting named variable list will either be of domain scope (when the domainId argument
+ * is present) or VMD scope when the domainId argument is NULL.
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param domainId the domain name of the requested variables.
+ * \param listName the name of the named variable list
+ * \param specWithResult if specWithResult is set to true, a IEC 61850 compliant request will be sent.
+ *
+ * \return Returns a MmsValue object or NULL if the request failed. The MmsValue object is
+ * is of type MMS_ARRAY and contains the variable values of simple or complex type
+ * in the order as they appeared in named variable list definition.
+ */
+LIB61850_API MmsValue*
+MmsConnection_readNamedVariableListValues(MmsConnection self, MmsError* mmsError, const char* domainId,
+        const char* listName, bool specWithResult);
+
+LIB61850_API void
+MmsConnection_readNamedVariableListValuesAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
+        const char* domainId, const char* listName, bool specWithResult,
+        MmsConnection_ReadVariableHandler handler, void* parameter);
+
+
+/**
+ * \brief Read the values of a association specific named variable list
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param listName the name of the named variable list
+ * \param specWithResult if specWithResult is set to true, a IEC 61850 compliant request will be sent.
+ *
+ * \return Returns a MmsValue object or NULL if the request failed. The MmsValue object is
+ * is of type MMS_ARRAY and contains the variable values of simple or complex type
+ * in the order as they appeared in named variable list definition.
+ */
+LIB61850_API MmsValue*
+MmsConnection_readNamedVariableListValuesAssociationSpecific(MmsConnection self, MmsError* mmsError,
+        const char* listName, bool specWithResult);
+
+LIB61850_API void
+MmsConnection_readNamedVariableListValuesAssociationSpecificAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
+        const char* listName, bool specWithResult,
+        MmsConnection_ReadVariableHandler handler, void* parameter);
+
+/**
+ * \brief Define a new VMD or domain scoped named variable list at the server.
+ *
+ * The resulting named variable list will either be of domain scope (when the domainId argument
+ * is present) or VMD scope when the domainId argument is NULL.
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param domainId the domain name of the domain for the new variable list
+ * \param listName the name of the named variable list
+ * \param variableSpecs a list of variable specifications for the new variable list. The list
+ *        elements have to be of type MmsVariableAccessSpecification*.
+ */
+LIB61850_API void
+MmsConnection_defineNamedVariableList(MmsConnection self, MmsError* mmsError, const char* domainId,
+        const char* listName,	LinkedList variableSpecs);
+
+LIB61850_API void
+MmsConnection_defineNamedVariableListAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId,
+        const char* listName, LinkedList variableSpecs,
+        MmsConnection_GenericServiceHandler handler, void* parameter);
+
+
+
+/**
+ * \brief Define a new association specific named variable list at the server.
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param listName the name of the named variable list
+ * \param variableSpecs list of variable specifications for the new variable list.The list
+ *        elements have to be of type MmsVariableAccessSpecification*.
+ */
+LIB61850_API void
+MmsConnection_defineNamedVariableListAssociationSpecific(MmsConnection self, MmsError* mmsError,
+        const char* listName,	LinkedList variableSpecs);
+
+LIB61850_API void
+MmsConnection_defineNamedVariableListAssociationSpecificAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
+        const char* listName, LinkedList variableSpecs,
+        MmsConnection_GenericServiceHandler handler, void* parameter);
+
+/**
+ * \brief Read the entry list of a named variable list at the server.
+ *
+ * The resulting named variable list will either be of domain scope (when the domainId argument
+ * is present) or VMD scope when the domainId argument is NULL.
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param domainId the domain name of the domain of the variable list
+ * \param listName the name of the named variable list
+ * \param deletable THIS IS A OUTPUT PARAMETER - indicates if the variable list is deletable by the
+ * client. The user may provide a NULL pointer if the value doesn't matter.
+ *
+ * \return List of names of the variable list entries or NULL if the request failed
+ */
+LIB61850_API LinkedList /* <MmsVariableAccessSpecification*> */
+MmsConnection_readNamedVariableListDirectory(MmsConnection self, MmsError* mmsError,
+        const char* domainId, const char* listName, bool* deletable);
+
+
+typedef void
+(*MmsConnection_ReadNVLDirectoryHandler) (uint32_t invokeId, void* parameter, MmsError mmsError, LinkedList /* <MmsVariableAccessSpecification*> */ specs, bool deletable);
+
+
+LIB61850_API void
+MmsConnection_readNamedVariableListDirectoryAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
+        const char* domainId, const char* listName,
+        MmsConnection_ReadNVLDirectoryHandler handler, void* parameter);
+
+
+/**
+ * \brief Read the entry list of an association specific named variable list at the server.
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param listName the name of the named variable list
+ *
+ * \return List of names of the variable list entries or NULL if the request failed
+ */
+LIB61850_API LinkedList /* <MmsVariableAccessSpecification*> */
+MmsConnection_readNamedVariableListDirectoryAssociationSpecific(MmsConnection self, MmsError* mmsError,
+        const char* listName, bool* deletable);
+
+LIB61850_API void
+MmsConnection_readNamedVariableListDirectoryAssociationSpecificAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
+        const char* listName,
+        MmsConnection_ReadNVLDirectoryHandler handler, void* parameter);
+
+/**
+ * \brief Delete a named variable list at the server.
+ *
+ * The resulting named variable list will either be of domain scope (when the domainId argument
+ * is present) or VMD scope when the domainId argument is NULL.
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param domainId the domain name of the domain of the variable list
+ * \param listName the name of the named variable list
+ *
+ * \return true if named variable list has been deleted, false otherwise
+ */
+LIB61850_API bool
+MmsConnection_deleteNamedVariableList(MmsConnection self, MmsError* mmsError, const char* domainId, const char* listName);
+
+
+LIB61850_API void
+MmsConnection_deleteNamedVariableListAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId, const char* listName,
+        MmsConnection_GenericServiceHandler handler, void* parameter);
+
+/**
+ * \brief Delete an association specific named variable list at the server.
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param listName the name of the named variable list
+ *
+ * \return true if named variable list has been deleted, false otherwise
+ */
+LIB61850_API bool
+MmsConnection_deleteAssociationSpecificNamedVariableList(MmsConnection self, MmsError* mmsError,
+        const char* listName);
+
+
+LIB61850_API void
+MmsConnection_deleteAssociationSpecificNamedVariableListAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* listName,
+        MmsConnection_GenericServiceHandler handler, void* parameter);
+
+/**
+ * \brief Create a new MmsVariableSpecification that can be used to define named variable lists.
+ *
+ * The created object can be deleted with free(). If the parameter strings were dynamically
+ * allocated the deallocation is in the responsibility of the user.
+ *
+ * \param domainId the MMS domain name of the variable
+ * \param itemId the name for the MMS variable
+ *
+ * \return reference to the new MmsVariableSpecfication object
+ */
+LIB61850_API MmsVariableAccessSpecification*
+MmsVariableAccessSpecification_create(char* domainId, char* itemId);
+
+/**
+ * \brief Create a new MmsVariableSpecification that can be used to define named variable lists.
+ *
+ * The created object can be deleted with free(). If the parameter strings were dynamically
+ * allocated the deallocation is in the responsibility of the user. This function should be
+ * used for named variable list entries that are array elements or components of array elements
+ * in the case when the array element is of complex (structured) type.
+ *
+ * \param domainId the MMS domain name of the variable
+ * \param itemId the name for the MMS variable
+ * \param index the array index to describe an array element
+ * \param componentName the name of the component of the array element. Should be set to NULL
+ *        if the array element is of simple type or the whole array element is required.
+ *
+ * \return reference to the new MmsVariableSpecfication object
+ */
+LIB61850_API MmsVariableAccessSpecification*
+MmsVariableAccessSpecification_createAlternateAccess(char* domainId, char* itemId, int32_t index,
+		char* componentName);
+
+/**
+ * \brief Delete the MmsVariableAccessSpecification data structure
+ *
+ * \param self the instance to delete
+ */
+LIB61850_API void
+MmsVariableAccessSpecification_destroy(MmsVariableAccessSpecification* self);
+
+/**
+ * \brief Get the MMS local detail parameter (local detail means maximum MMS PDU size).
+ *
+ * This defaults to 65000 (or the value specified in the stack_config.h file.
+ * This function should not be called after a successful connection attempt.
+ *
+ * \param  self MmsConnection instance to operate on
+ * \param localDetail the maximum size of the MMS PDU that will be accepted.
+ */
+LIB61850_API void
+MmsConnection_setLocalDetail(MmsConnection self, int32_t localDetail);
+
+LIB61850_API int32_t
+MmsConnection_getLocalDetail(MmsConnection self);
+
+/**
+ * \brief get the identity of the connected server
+ *
+ * This function will return the identity of the server if the server supports the MMS identify service.
+ * The server identity consists of a vendor name, model name, and a revision.
+ *
+ * \param  self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ */
+LIB61850_API MmsServerIdentity*
+MmsConnection_identify(MmsConnection self, MmsError* mmsError);
+
+typedef void
+(*MmsConnection_IdentifyHandler) (uint32_t invokeId, void* parameter, MmsError mmsError,
+        char* vendorName, char* modelName, char* revision);
+
+LIB61850_API void
+MmsConnection_identifyAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
+        MmsConnection_IdentifyHandler handler, void* parameter);
+
+/**
+ * \brief Destroy (free) an MmsServerIdentity object
+ *
+ * \param self the object to destroy
+ */
+LIB61850_API void
+MmsServerIdentity_destroy(MmsServerIdentity* self);
+
+/**
+ * \brief get the VMD status of the connected server (is MMS status service)
+ *
+ * This function will return the status of the connected server by invoking the MMS status service.
+ * The services returns the logical and physical states of the server.
+ *
+ * \param[in] self MmsConnection instance to operate on
+ * \param[out] mmsError user provided variable to store error code
+ * \param[out] vmdLogicalStatus user provided variable to store the logical state of the VMD
+ * \param[out] vmdPhysicalStatus user provided variable to store the physical state of the VMD
+ * \param[in] extendedDerivation instructs the server to invoke self-diagnosis routines to determine server status
+ */
+LIB61850_API void
+MmsConnection_getServerStatus(MmsConnection self, MmsError* mmsError, int* vmdLogicalStatus, int* vmdPhysicalStatus,
+        bool extendedDerivation);
+
+typedef void
+(*MmsConnection_GetServerStatusHandler) (uint32_t invokeId, void* parameter, MmsError mmsError, int vmdLogicalStatus, int vmdPhysicalStatus);
+
+LIB61850_API void
+MmsConnection_getServerStatusAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, bool extendedDerivation,
+        MmsConnection_GetServerStatusHandler handler, void* parameter);
+
+/*******************************************************************************
+ * functions for MMS file services
+ *******************************************************************************/
+
+typedef void
+(*MmsFileDirectoryHandler) (void* parameter, char* filename, uint32_t size, uint64_t lastModified);
+
+/**
+ * \brief Callback handler for the get file directory service
+ *
+ * Will be called once for each file directory entry and after the last entry with \ref filename = NULL to indicate
+ * with \ref moreFollows set to true when more data is available server side. In case of an error the callback will be called with
+ * \ref mmsError != MMS_ERROR_NONE and moreFollows = false.
+ */
+typedef void
+(*MmsConnection_FileDirectoryHandler) (uint32_t invokeId, void* parameter, MmsError mmsError, char* filename, uint32_t size, uint64_t lastModfified,
+        bool moreFollows);
+
+typedef void
+(*MmsFileReadHandler) (void* parameter, int32_t frsmId, uint8_t* buffer, uint32_t bytesReceived);
+
+/**
+ * \brief Callback handler for the file read service
+ *
+ * Will be called for every received part of the file and when there is an error during reading the file.
+ *
+ * \param invokeId invokeID of the response
+ * \param parameter user provided context parameter
+ * \param mmsError error code
+ * \param frsmId ID of the file
+ * \param buffer buffer where the received bytes are stored
+ * \param bytesReceived number of bytes received with this response
+ * \param moreFollows more messages with parts of the file are following
+ */
+typedef void
+(*MmsConnection_FileReadHandler) (uint32_t invokeId, void* parameter, MmsError mmsError, int32_t frsmId, uint8_t* buffer, uint32_t byteReceived,
+        bool moreFollows);
+
+
+/**
+ * \brief open a file for read
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ *
+ * \return the FRSM ID (file read state machine) handle of the opened file
+ */
+LIB61850_API int32_t
+MmsConnection_fileOpen(MmsConnection self, MmsError* mmsError, const char* filename, uint32_t initialPosition,
+        uint32_t* fileSize, uint64_t* lastModified);
+
+typedef void
+(*MmsConnection_FileOpenHandler) (uint32_t invokeId, void* parameter, MmsError mmsError, int32_t frsmId, uint32_t fileSize, uint64_t lastModified);
+
+LIB61850_API void
+MmsConnection_fileOpenAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* filename, uint32_t initialPosition, MmsConnection_FileOpenHandler handler,
+        void* parameter);
+
+
+/**
+ * \brief read the next data block from the file
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param frsmId the FRSM ID (file read state machine) handle of the file
+ * \param handler callback that is invoked to deliver the received data
+ * \param handlerParameter user provided paramter that is passed to the callback function
+ *
+ * \return true if more data follows, false if last data has been received.
+ */
+LIB61850_API bool
+MmsConnection_fileRead(MmsConnection self, MmsError* mmsError, int32_t frsmId, MmsFileReadHandler handler, void* handlerParameter);
+
+LIB61850_API void
+MmsConnection_fileReadAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, int32_t frsmId, MmsConnection_FileReadHandler handler, void* parameter);
+
+/**
+ * \brief close the file with the specified frsmID
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param frsmId id of the file to close
+ */
+LIB61850_API void
+MmsConnection_fileClose(MmsConnection self, MmsError* mmsError, int32_t frsmId);
+
+LIB61850_API void
+MmsConnection_fileCloseAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, uint32_t frsmId, MmsConnection_GenericServiceHandler handler, void* parameter);
+
+/**
+ * \brief delete the file with the specified name
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param fileName name of the file to delete
+ */
+LIB61850_API void
+MmsConnection_fileDelete(MmsConnection self, MmsError* mmsError, const char* fileName);
+
+LIB61850_API void
+MmsConnection_fileDeleteAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* fileName,
+        MmsConnection_GenericServiceHandler handler, void* parameter);
+
+/**
+ * \brief rename the file with the specified name
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param currentFileName name of the file to rename
+ * \param newFileName new name of the file
+ */
+LIB61850_API void
+MmsConnection_fileRename(MmsConnection self, MmsError* mmsError, const char* currentFileName, const char* newFileName);
+
+LIB61850_API void
+MmsConnection_fileRenameAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* currentFileName, const char* newFileName,
+        MmsConnection_GenericServiceHandler handler, void* parameter);
+
+/**
+ * \brief Send an obtainFile request to the server (used to initiate file download to server)
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param sourceFile the name of the source file (client side name)
+ * \param destinationFile the name of the destination file (server side name)
+ */
+LIB61850_API void
+MmsConnection_obtainFile(MmsConnection self, MmsError* mmsError, const char* sourceFile, const char* destinationFile);
+
+LIB61850_API void
+MmsConnection_obtainFileAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* sourceFile, const char* destinationFile,
+        MmsConnection_GenericServiceHandler handler, void* parameter);
+
+/**
+ * \brief get the file directory of the server.
+ *
+ * This function will return the directory entries of the given server directory. For each directory entry
+ * the provided callback handler is called. If the
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param fileSpecification the file specification of the directory to browse or NULL to browse the root directory
+ * \param continueAfter continuation point or NULL for the first request. The continuation point is the first entry
+ *                      after the provided continuation file name.
+ * \param handler user provided callback handler
+ * \param handlerParameter user provided parameter that is passed to the handler
+ *
+ * \return (more follows) true if more data is available
+ */
+LIB61850_API bool
+MmsConnection_getFileDirectory(MmsConnection self, MmsError* mmsError, const char* fileSpecification, const char* continueAfter,
+        MmsFileDirectoryHandler handler, void* handlerParameter);
+
+LIB61850_API void
+MmsConnection_getFileDirectoryAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* fileSpecification, const char* continueAfter,
+        MmsConnection_FileDirectoryHandler handler, void* parameter);
+
+typedef struct sMmsJournalEntry* MmsJournalEntry;
+
+typedef struct sMmsJournalVariable* MmsJournalVariable;
+
+struct sMmsJournalEntry {
+    MmsValue* entryID; /* type MMS_OCTET_STRING */
+    MmsValue* occurenceTime; /* type MMS_BINARY_TIME */
+    LinkedList journalVariables;
+};
+
+struct sMmsJournalVariable {
+    char* tag;
+    MmsValue* value;
+};
+
+/**
+ * \brief Destroy a single MmsJournalEntry instance.
+ *
+ * This function will destroy the whole MmsJournalEntry object including the attached list
+ * of MmsJournalVariable objects. It is intended to be used in conjunction with the
+ * LinkedList_destroyDeep function in order to free the result of MmsConnection_readJournalTimeRange
+ * or MmsConnection_readJournalStartAfter
+ *
+ * LinkedList_destroyDeep(journalEntries, (LinkedListValueDeleteFunction)
+ *                           MmsJournalEntry_destroy);
+ *
+ * \param self the MmsJournalEntry instance to destroy
+ */
+LIB61850_API void
+MmsJournalEntry_destroy(MmsJournalEntry self);
+
+LIB61850_API MmsValue*
+MmsJournalEntry_getEntryID(MmsJournalEntry self);
+
+LIB61850_API MmsValue*
+MmsJournalEntry_getOccurenceTime(MmsJournalEntry self);
+
+LIB61850_API LinkedList /* <MmsJournalVariable> */
+MmsJournalEntry_getJournalVariables(MmsJournalEntry self);
+
+LIB61850_API const char*
+MmsJournalVariable_getTag(MmsJournalVariable self);
+
+LIB61850_API MmsValue*
+MmsJournalVariable_getValue(MmsJournalVariable self);
+
+typedef void
+(*MmsConnection_ReadJournalHandler) (uint32_t invokeId, void* parameter, MmsError mmsError, LinkedList /* <MmsJournalEntry> */ journalEntries, bool moreFollows);
+
+
+LIB61850_API LinkedList /* <MmsJournalEntry> */
+MmsConnection_readJournalTimeRange(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId,
+        MmsValue* startTime, MmsValue* endTime, bool* moreFollows);
+
+LIB61850_API void
+MmsConnection_readJournalTimeRangeAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId, const char* itemId,
+        MmsValue* startTime, MmsValue* endTime, MmsConnection_ReadJournalHandler handler, void* parameter);
+
+LIB61850_API LinkedList /* <MmsJournalEntry> */
+MmsConnection_readJournalStartAfter(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId,
+        MmsValue* timeSpecification, MmsValue* entrySpecification, bool* moreFollows);
+
+LIB61850_API void
+MmsConnection_readJournalStartAfterAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId, const char* itemId,
+        MmsValue* timeSpecification, MmsValue* entrySpecification, MmsConnection_ReadJournalHandler handler, void* parameter);
+
+/**@}*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MMS_CLIENT_CONNECTION_H_ */

+ 181 - 0
library/include/libiec61850/mms_common.h

@@ -0,0 +1,181 @@
+/*
+ *  mms_common.h
+ *
+ *  Copyright 2013-2018 Michael Zillgith
+ *
+ *  This file is part of libIEC61850.
+ *
+ *  libIEC61850 is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  libIEC61850 is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with libIEC61850.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#include "libiec61850_common_api.h"
+
+#ifndef MMS_COMMON_H_
+#define MMS_COMMON_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \addtogroup common_api_group
+ */
+/**@{*/
+
+typedef enum
+{
+    /* generic error codes */
+    MMS_ERROR_NONE = 0,
+    MMS_ERROR_CONNECTION_REJECTED = 1,
+    MMS_ERROR_CONNECTION_LOST = 2,
+    MMS_ERROR_SERVICE_TIMEOUT = 3,
+    MMS_ERROR_PARSING_RESPONSE = 4,
+    MMS_ERROR_HARDWARE_FAULT = 5,
+    MMS_ERROR_CONCLUDE_REJECTED = 6,
+    MMS_ERROR_INVALID_ARGUMENTS = 7,
+    MMS_ERROR_OUTSTANDING_CALL_LIMIT = 8,
+
+    MMS_ERROR_OTHER = 9,
+
+    /* confirmed error PDU codes */
+    MMS_ERROR_VMDSTATE_OTHER = 10,
+
+    MMS_ERROR_APPLICATION_REFERENCE_OTHER = 20,
+
+    MMS_ERROR_DEFINITION_OTHER = 30,
+    MMS_ERROR_DEFINITION_INVALID_ADDRESS = 31,
+    MMS_ERROR_DEFINITION_TYPE_UNSUPPORTED = 32,
+    MMS_ERROR_DEFINITION_TYPE_INCONSISTENT = 33,
+    MMS_ERROR_DEFINITION_OBJECT_UNDEFINED = 34,
+    MMS_ERROR_DEFINITION_OBJECT_EXISTS = 35,
+    MMS_ERROR_DEFINITION_OBJECT_ATTRIBUTE_INCONSISTENT = 36,
+
+    MMS_ERROR_RESOURCE_OTHER = 40,
+    MMS_ERROR_RESOURCE_CAPABILITY_UNAVAILABLE = 41,
+
+    MMS_ERROR_SERVICE_OTHER = 50,
+    MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT = 55,
+
+    MMS_ERROR_SERVICE_PREEMPT_OTHER = 60,
+
+    MMS_ERROR_TIME_RESOLUTION_OTHER = 70,
+
+    MMS_ERROR_ACCESS_OTHER = 80,
+    MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT = 81,
+    MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED = 82,
+    MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED = 83,
+    MMS_ERROR_ACCESS_OBJECT_INVALIDATED = 84,
+    MMS_ERROR_ACCESS_OBJECT_VALUE_INVALID = 85, /* for DataAccessError 11 */
+    MMS_ERROR_ACCESS_TEMPORARILY_UNAVAILABLE = 86, /* for DataAccessError 2 */
+
+    MMS_ERROR_FILE_OTHER = 90,
+    MMS_ERROR_FILE_FILENAME_AMBIGUOUS = 91,
+    MMS_ERROR_FILE_FILE_BUSY = 92,
+    MMS_ERROR_FILE_FILENAME_SYNTAX_ERROR = 93,
+    MMS_ERROR_FILE_CONTENT_TYPE_INVALID = 94,
+    MMS_ERROR_FILE_POSITION_INVALID = 95,
+    MMS_ERROR_FILE_FILE_ACCESS_DENIED = 96,
+    MMS_ERROR_FILE_FILE_NON_EXISTENT = 97,
+    MMS_ERROR_FILE_DUPLICATE_FILENAME = 98,
+    MMS_ERROR_FILE_INSUFFICIENT_SPACE_IN_FILESTORE = 99,
+
+    /* reject codes */
+    MMS_ERROR_REJECT_OTHER = 100,
+    MMS_ERROR_REJECT_UNKNOWN_PDU_TYPE = 101,
+    MMS_ERROR_REJECT_INVALID_PDU = 102,
+    MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE = 103,
+    MMS_ERROR_REJECT_UNRECOGNIZED_MODIFIER = 104,
+    MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT = 105
+
+} MmsError;
+
+typedef enum
+{
+    /*! this represents all MMS array types (arrays contain uniform elements) */
+    MMS_ARRAY = 0,
+    /*! this represents all complex MMS types (structures) */
+    MMS_STRUCTURE = 1,
+    /*! boolean value */
+    MMS_BOOLEAN = 2,
+    /*! bit string */
+    MMS_BIT_STRING = 3,
+    /*! represents all signed integer types */
+    MMS_INTEGER = 4,
+    /*! represents all unsigned integer types */
+    MMS_UNSIGNED = 5,
+    /*! represents all float type (32 and 64 bit) */
+    MMS_FLOAT = 6,
+    /*! octet string (unstructured bytes) */
+    MMS_OCTET_STRING = 7,
+    /*! MMS visible string */
+    MMS_VISIBLE_STRING = 8,
+    MMS_GENERALIZED_TIME = 9,
+    MMS_BINARY_TIME = 10,
+    MMS_BCD = 11,
+    MMS_OBJ_ID = 12,
+    /*! MMS unicode string */
+    MMS_STRING = 13,
+    /*! MMS UTC time type */
+    MMS_UTC_TIME = 14,
+    /*! This represents an error code as returned by MMS read services */
+    MMS_DATA_ACCESS_ERROR = 15
+} MmsType;
+
+typedef struct sMmsDomain MmsDomain;
+
+typedef struct sMmsAccessSpecifier
+{
+    MmsDomain* domain;
+    char* variableName;
+    int arrayIndex; /* -1 --> no index present / ignore index */
+    char* componentName;
+} MmsAccessSpecifier;
+
+typedef struct
+{
+    char* domainId;
+    char* itemId;
+    int32_t arrayIndex; /* -1 --> no index present / ignore index */
+    char* componentName;
+} MmsVariableAccessSpecification;
+
+typedef struct sMmsNamedVariableList* MmsNamedVariableList;
+typedef struct sMmsAccessSpecifier* MmsNamedVariableListEntry;
+
+/**
+ * \brief ITU (International Telecommunication Union) object identifier (OID)
+ */
+typedef struct {
+    uint16_t arc[10];
+    int arcCount;
+} ItuObjectIdentifier;
+
+/**
+ * \brief ISO application reference (specifies an ISO application endpoint)
+ */
+typedef struct {
+    ItuObjectIdentifier apTitle;
+    int aeQualifier;
+} IsoApplicationReference;
+
+/**@}*/
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MMS_COMMON_H_ */

+ 422 - 0
library/include/libiec61850/mms_server.h

@@ -0,0 +1,422 @@
+/*
+ *  mms_server.h
+ *
+ *  Copyright 2013-2023 Michael Zillgith
+ *
+ *  This file is part of libIEC61850.
+ *
+ *  libIEC61850 is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  libIEC61850 is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with libIEC61850.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef MMS_SERVER_H_
+#define MMS_SERVER_H_
+
+/** \addtogroup mms_server_api_group
+ *  @{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "mms_value.h"
+#include "iso_connection_parameters.h"
+
+typedef enum {
+	MMS_SERVER_NEW_CONNECTION,
+	MMS_SERVER_CONNECTION_CLOSED,
+	MMS_SERVER_CONNECTION_TICK
+} MmsServerEvent;
+
+typedef struct sMmsServer* MmsServer;
+
+typedef struct sMmsServerConnection* MmsServerConnection;
+
+typedef enum {
+    MMS_DOMAIN_SPECIFIC,
+    MMS_ASSOCIATION_SPECIFIC,
+    MMS_VMD_SPECIFIC
+} MmsVariableListType;
+
+LIB61850_INTERNAL void
+MmsServer_setLocalIpAddress(MmsServer self, const char* localIpAddress);
+
+LIB61850_INTERNAL bool
+MmsServer_isRunning(MmsServer self);
+
+typedef enum {
+    MMS_VARLIST_CREATE,
+    MMS_VARLIST_DELETE,
+    MMS_VARLIST_READ,
+    MMS_VARLIST_WRITE,
+    MMS_VARLIST_GET_DIRECTORY
+} MmsVariableListAccessType;
+
+/**
+ * \brief callback handler that is called for each named variable list access
+ *
+ * \param parameter a user provided parameter
+ * \param accessType the kind of access (create, delete, read, write, get directory)
+ * \param listType the type (scope) of the named variable list (either domain, association or VMD specific)
+ * \param domain the MMS domain the list is belonging to (is NULL for association or VMD specific lists!)
+ * \param listName the name
+ * \param connection client connection that is accessing the named variable list
+ *
+ * \return MMS_ERROR_NONE if the request is accepted, otherwise the MmsError value that has to be sent back to the client
+ */
+typedef MmsError (*MmsNamedVariableListAccessHandler)(void* parameter, MmsVariableListAccessType accessType, MmsVariableListType listType, MmsDomain* domain,
+        char* listName, MmsServerConnection connection);
+
+/**
+ * \brief Install callback handler that is called when a named variable list is accessed by a client
+ *
+ * \param self the MmsServer instance to operate on
+ * \param handler the callback handler function
+ * \param parameter user provided parameter that is passed to the callback handler
+ */
+LIB61850_INTERNAL void
+MmsServer_installVariableListAccessHandler(MmsServer self, MmsNamedVariableListAccessHandler handler, void* parameter);
+
+/**
+ * \brief callback handler that is called for each received read journal request
+ * 
+ * \param parameter a user provided parameter
+ * \param domain the MMS domain the journal is belonging to
+ * \param logName the name of the journal
+ * \param connection client connection that is accessing the journal
+ */
+typedef bool (*MmsReadJournalHandler)(void* parameter, MmsDomain* domain, const char* logName, MmsServerConnection connection);
+
+/**
+ * \brief Install callback handler that is called when a journal is accessed by a client
+ *
+ * \param self the MmsServer instance to operate on
+ * \param handler the callback handler function
+ * \param parameter user provided parameter that is passed to the callback handler
+ */
+LIB61850_INTERNAL void
+MmsServer_installReadJournalHandler(MmsServer self, MmsReadJournalHandler handler, void* parameter);
+
+typedef enum {
+    MMS_GETNAMELIST_DOMAINS,
+    MMS_GETNAMELIST_JOURNALS,
+    MMS_GETNAMELIST_DATASETS,
+    MMS_GETNAMELIST_DATA
+} MmsGetNameListType;
+
+typedef bool (*MmsGetNameListHandler)(void* parameter, MmsGetNameListType nameListType, MmsDomain* domain, MmsServerConnection connection);
+
+LIB61850_INTERNAL void
+MmsServer_installGetNameListHandler(MmsServer self, MmsGetNameListHandler handler, void* parameter);
+
+/**
+ * \brief ObtainFile service callback handler
+ *
+ * This is invoked when the obtainFile service is requested by the client. It can be used to accept or deny the
+ * write access to the file system based on the client connection.
+ *
+ * \param parameter user provided parameter that is passed to the callback handler
+ * \param connection the connection that requested the service
+ * \param sourceFilename the source file name on the client side system
+ * \param destinationFilename the target file name on the server side system
+ */
+typedef bool (*MmsObtainFileHandler)(void* parameter, MmsServerConnection connection, const char* sourceFilename, const char* destinationFilename);
+
+/**
+ * \brief Install callback handler that is invoked when the file upload (obtainFile service) is invoked by the client
+ *
+ * This handler can be used to apply access restrictions on the file upload (obtainFile) service
+ *
+ * \param self the MmsServer instance to operate on
+ * \param handler the callback handler function
+ * \param parameter user provided parameter that is passed to the callback handler
+ */
+LIB61850_INTERNAL void
+MmsServer_installObtainFileHandler(MmsServer self, MmsObtainFileHandler handler, void* parameter);
+
+/**
+ * \brief Get file complete (obtainFile service terminated) callback handler
+ *
+ * This handler can be used to invoke some actions after a file upload is complete.
+ *
+ * \param parameter user provided parameter that is passed to the callback handler
+ * \param connection the connection that requested the service
+ * \param destinationFilename the target file name on the server side system
+ */
+typedef void (*MmsGetFileCompleteHandler)(void* parameter, MmsServerConnection connection, const char* destinationFilename);
+
+/**
+ * \brief Install callback handler that is invoked when the file upload (obtainFile service) is completed and the
+ *        file has been uploaded.
+ *
+ * \param self the MmsServer instance
+ * \param handler the callback handler function
+ * \param parameter user provided parameter that is passed to the callback handler
+ */
+LIB61850_INTERNAL void
+MmsServer_installGetFileCompleteHandler(MmsServer self, MmsGetFileCompleteHandler handler, void* parameter);
+
+
+typedef  enum {
+    MMS_FILE_ACCESS_TYPE_READ_DIRECTORY,
+    MMS_FILE_ACCESS_TYPE_OPEN,
+    MMS_FILE_ACCESS_TYPE_OBTAIN,
+    MMS_FILE_ACCESS_TYPE_DELETE,
+    MMS_FILE_ACCESS_TYPE_RENAME
+} MmsFileServiceType;
+
+/**
+ * \brief MmsFileAccessHandler callback function. Use to monitor and control file access
+ *
+ * \param parameter user provided parameter that is passed to the callback handler
+ * \param connection the connection that requested the service
+ * \param service the requested file service
+ * \param localFilename the requested file or directory name at the server
+ * \param otherFilename a second file name parameter (e.g. source file of the ObtainFile or new file of rename file)
+ *
+ * \return MMS_ERROR_NONE when the request is accepted, otherwise use the appropriate error code (e.g. MMS_ERROR_FILE_FILE_ACCESS_DENIED)
+ */
+typedef MmsError (*MmsFileAccessHandler) (void* parameter, MmsServerConnection connection, MmsFileServiceType service,
+                                          const char* localFilename, const char* otherFilename);
+
+
+/**
+ * \brief Install a callback handler this is invoked when the client requests a file server. This function can be
+ *        used to monitor and control file access
+ *
+ * \param self the MmsServer instance
+ * \param handler the callback handler function
+ * \param parameter user provided parameter that is passed to the callback handler
+ */
+LIB61850_API void
+MmsServer_installFileAccessHandler(MmsServer self, MmsFileAccessHandler handler, void* parameter);
+
+/**
+ * \brief Set the virtual filestore basepath for the MMS file services
+ *
+ * All external file service accesses will be mapped to paths relative to the base directory.
+ * NOTE: This function is only available when the CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME
+ * option in stack_config.h is set.
+ *
+ * \param self the MmsServer instance
+ * \param basepath the new virtual filestore basepath
+ */
+LIB61850_INTERNAL void
+MmsServer_setFilestoreBasepath(MmsServer self, const char* basepath);
+
+/**
+ * \brief Set the maximum number of TCP client connections
+ *
+ * \param[in] maxConnections the maximum number of TCP client connections to accept
+ */
+LIB61850_INTERNAL void
+MmsServer_setMaxConnections(MmsServer self, int maxConnections);
+
+/**
+ * \brief Enable/disable MMS file services at runtime
+ *
+ * NOTE: requires CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME = 1 in stack configuration
+ *
+ * \param[in] self the MmsServer instance
+ * \param[in] enable true to enable file services, false to disable
+ */
+LIB61850_INTERNAL void
+MmsServer_enableFileService(MmsServer self, bool enable);
+
+/**
+ * \brief Enable/disable dynamic named variable list (data set) service
+ *
+ * NOTE: requires CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME = 1 in stack configuration
+ *
+ * \param[in] self the MmsServer instance
+ * \param[in] enable true to enable named variable list services, false to disable
+ */
+LIB61850_INTERNAL void
+MmsServer_enableDynamicNamedVariableListService(MmsServer self, bool enable);
+
+/**
+ * \brief Set the maximum number of association specific data sets (per connection)
+ *
+ * \param[in] self the MmsServer instance
+ * \param[in] maxDataSets maximum number association specific data sets
+ */
+LIB61850_INTERNAL void
+MmsServer_setMaxAssociationSpecificDataSets(MmsServer self, int maxDataSets);
+
+/**
+ * \brief Set the maximum number of domain specific data sets
+ *
+ * \param[in] self the MmsServer instance
+ * \param[in] maxDataSets maximum number domain specific data sets
+ */
+LIB61850_INTERNAL void
+MmsServer_setMaxDomainSpecificDataSets(MmsServer self, int maxDataSets);
+
+/**
+ * \brief Set the maximum number of data set entries (for dynamic data sets)
+ *
+ * \param[in] self the MmsServer instance
+ * \param[in] maxDataSetEntries maximum number of dynamic data set entries
+ */
+LIB61850_INTERNAL void
+MmsServer_setMaxDataSetEntries(MmsServer self, int maxDataSetEntries);
+
+/**
+ * \brief Enable/disable journal service
+ *
+ * NOTE: requires CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME = 1 in stack configuration
+ *
+ * \param[in] self the MmsServer instance
+ * \param[in] enable true to enable journal service, false to disable
+ */
+LIB61850_INTERNAL void
+MmsServer_enableJournalService(MmsServer self, bool enable);
+
+
+
+
+
+/***************************************************
+ * Functions for MMS identify service
+ ***************************************************/
+
+/**
+ * \brief set the values that the server will give as response for an MMS identify request
+ *
+ * With this function the VMD identity attributes can be set programmatically. If not set by this
+ * function the default values form stack_config.h are used.
+ *
+ * \param self the MmsServer instance to operate on
+ * \param vendorName the vendor name attribute of the VMD
+ * \param modelName the model name attribute of the VMD
+ * \param revision the revision attribute of the VMD
+ */
+LIB61850_INTERNAL void
+MmsServer_setServerIdentity(MmsServer self, char* vendorName, char* modelName, char* revision);
+
+/**
+ * \brief get the vendor name attribute of the VMD identity
+ *
+ * \param self the MmsServer instance to operate on
+ * \return the vendor name attribute of the VMD as C string
+ */
+LIB61850_INTERNAL char*
+MmsServer_getVendorName(MmsServer self);
+
+/**
+ * \brief get the model name attribute of the VMD identity
+ *
+ * \param self the MmsServer instance to operate on
+ * \return the model name attribute of the VMD as C string
+ */
+LIB61850_INTERNAL char*
+MmsServer_getModelName(MmsServer self);
+
+/**
+ * \brief get the revision attribute of the VMD identity
+ *
+ * \param self the MmsServer instance to operate on
+ * \return the revision attribute of the VMD as C string
+ */
+LIB61850_INTERNAL char*
+MmsServer_getRevision(MmsServer self);
+
+/***************************************************
+ * Functions for MMS status service
+ ***************************************************/
+
+#define MMS_LOGICAL_STATE_STATE_CHANGES_ALLOWED 0
+#define MMS_LOGICAL_STATE_NO_STATE_CHANGES_ALLOWED 1
+#define MMS_LOGICAL_STATE_LIMITED_SERVICES_PERMITTED 2
+#define MMS_LOGICAL_STATE_SUPPORT_SERVICES_ALLOWED 3
+
+#define MMS_PHYSICAL_STATE_OPERATIONAL 0
+#define MMS_PHYSICAL_STATE_PARTIALLY_OPERATIONAL 1
+#define MMS_PHYSICAL_STATE_INOPERATIONAL 2
+#define MMS_PHYSICAL_STATE_NEEDS_COMMISSIONING 3
+
+/**
+ * \brief User provided handler that is invoked on a MMS status request
+ *
+ * The extendedDerivation parameter indicates that the client requests the server to perform
+ * self diagnosis tests before answering the request.
+ *
+ * \param parameter is a user provided parameter
+ * \param mmsServer is the MmsServer instance
+ * \param connection is the MmsServerConnection instance that received the MMS status request
+ * \param extendedDerivation indicates if the request contains the extendedDerivation parameter
+ */
+typedef void (*MmsStatusRequestListener)(void* parameter, MmsServer mmsServer, MmsServerConnection connection, bool extendedDerivation);
+
+/**
+ * \brief set the VMD state values for the VMD status service
+ *
+ * \param self the MmsServer instance to operate on
+ * \param vmdLogicalStatus the logical status attribute of the VMD
+ * \param vmdPhysicalStatus the physical status attribute of the VMD
+ */
+LIB61850_INTERNAL void
+MmsServer_setVMDStatus(MmsServer self, int vmdLogicalStatus, int vmdPhysicalStatus);
+
+/**
+ * \brief get the logical status attribute of the VMD
+ *
+ * \param self the MmsServer instance to operate on
+ */
+LIB61850_INTERNAL int
+MmsServer_getVMDLogicalStatus(MmsServer self);
+
+/**
+ * \brief get the physical status attribute of the VMD
+ *
+ * \param self the MmsServer instance to operate on
+ */
+LIB61850_INTERNAL int
+MmsServer_getVMDPhysicalStatus(MmsServer self);
+
+/**
+ * \brief set the MmsStatusRequestListener for this MmsServer
+ *
+ * With this function the API user can register a listener that is invoked whenever a
+ * MMS status request is received from a client. Inside of the handler the user can
+ * provide new status values with the MmsServer_setVMDStatus function.
+ *
+ * \param self the MmsServer instance to operate on
+ * \param listener the listener that is called when a MMS status request is received
+ * \param parameter a user provided parameter that is handed over to the listener
+ */
+LIB61850_INTERNAL void
+MmsServer_setStatusRequestListener(MmsServer self, MmsStatusRequestListener listener, void* parameter);
+
+LIB61850_INTERNAL char*
+MmsServerConnection_getClientAddress(MmsServerConnection self);
+
+LIB61850_INTERNAL char*
+MmsServerConnection_getLocalAddress(MmsServerConnection self);
+
+LIB61850_INTERNAL void*
+MmsServerConnection_getSecurityToken(MmsServerConnection self);
+
+LIB61850_INTERNAL void
+MmsServer_ignoreClientRequests(MmsServer self, bool enable);;
+
+/**@}*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MMS_SERVER_H_ */

+ 164 - 0
library/include/libiec61850/mms_type_spec.h

@@ -0,0 +1,164 @@
+/*
+ *  mms_type_spec.h
+ *
+ *  MmsTypeSpecfication objects are used to describe simple and complex MMS types.
+ *  Complex types are arrays or structures of simple and complex types.
+ *  They also represent MMS NamedVariables.
+ *
+ *  Copyright 2013-2019 Michael Zillgith
+ *
+ *  This file is part of libIEC61850.
+ *
+ *  libIEC61850 is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  libIEC61850 is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with libIEC61850.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef MMS_TYPE_SPEC_H_
+#define MMS_TYPE_SPEC_H_
+
+#include "libiec61850_common_api.h"
+#include "mms_common.h"
+#include "mms_types.h"
+#include "linked_list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \addtogroup common_api_group
+ */
+/**@{*/
+
+/**
+ * \defgroup MMS_VAR_SPEC MmsVariableSpecification data type specifications
+ */
+/**@{*/
+
+/**
+ * \brief Delete MmsTypeSpecification object (recursive).
+ *
+ * \param self the MmsVariableSpecification instance
+ */
+LIB61850_API 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!)
+ *
+ */
+LIB61850_API MmsValue*
+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.
+ */
+LIB61850_API MmsVariableSpecification*
+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
+ */
+LIB61850_API MmsType
+MmsVariableSpecification_getType(MmsVariableSpecification* self);
+
+/**
+ * \brief Check if the value has exactly the same type as this variable specfication
+ *
+ * \param self the MmsVariableSpecification instance
+ * \param value the value to check
+ *
+ * \return true if type is matching, false otherwise
+ */
+LIB61850_API bool
+MmsVariableSpecification_isValueOfType(MmsVariableSpecification* self, const MmsValue* value);
+
+/**
+ * \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
+ */
+LIB61850_API const char*
+MmsVariableSpecification_getName(MmsVariableSpecification* self);
+
+LIB61850_API LinkedList /* <char*> */
+MmsVariableSpecification_getStructureElements(MmsVariableSpecification* self);
+
+/**
+ * \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
+ */
+LIB61850_API int
+MmsVariableSpecification_getSize(MmsVariableSpecification* self);
+
+LIB61850_API MmsVariableSpecification*
+MmsVariableSpecification_getChildSpecificationByIndex(MmsVariableSpecification* self, int index);
+
+/**
+ * \brief return the MmsVariableSpecification of a structure element with the given name
+ *
+ *  \param self the MmsVariableSpecification object
+ *  \param name the name of the component (structure element)
+ *  \param index (OUT) if not NULL the index of the structure element will be stored there
+ *
+ *  \return the type specification of the component or NULL if the component was not found
+ */
+LIB61850_API MmsVariableSpecification*
+MmsVariableSpecification_getChildSpecificationByName(MmsVariableSpecification* self, const char* name, int* index);
+
+LIB61850_API MmsVariableSpecification*
+MmsVariableSpecification_getArrayElementSpecification(MmsVariableSpecification* self);
+
+LIB61850_API int
+MmsVariableSpecification_getExponentWidth(MmsVariableSpecification* self);
+
+/**@}*/
+
+/**@}*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MMS_TYPE_SPEC_H_ */

+ 81 - 0
library/include/libiec61850/mms_types.h

@@ -0,0 +1,81 @@
+/*
+ *  mms_types.h
+ *
+ *  Copyright 2013, 2014 Michael Zillgith
+ *
+ *	This file is part of libIEC61850.
+ *
+ *	libIEC61850 is free software: you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation, either version 3 of the License, or
+ *	(at your option) any later version.
+ *
+ *	libIEC61850 is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with libIEC61850.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *	See COPYING file for the complete license text.
+ */
+
+#ifndef MMS_TYPES_H_
+#define MMS_TYPES_H_
+
+#include "libiec61850_common_api.h"
+
+typedef enum  {
+    MMS_VALUE_NO_RESPONSE,
+	MMS_VALUE_OK,
+	MMS_VALUE_ACCESS_DENIED,
+	MMS_VALUE_VALUE_INVALID,
+	MMS_VALUE_TEMPORARILY_UNAVAILABLE,
+	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;
+    union uMmsTypeSpecification
+    {
+        struct sMmsArray {
+            int elementCount; /* number of array elements */
+            MmsVariableSpecification* elementTypeSpec;
+        } array;
+        struct sMmsStructure {
+            int elementCount;
+            MmsVariableSpecification** elements;
+        } structure;
+        int boolean; /* dummy - not required */
+        int integer; /* size of integer in bits */
+        int unsignedInteger; /* size of integer in bits */
+        struct sMmsFloat
+        {
+            uint8_t exponentWidth;
+            uint8_t formatWidth;
+        } floatingpoint;
+        int bitString; /* Number of bits in bitstring */
+        int octetString; /* Number of octets in octet string */
+        int visibleString; /* Maximum size of string */
+        int mmsString;
+        int utctime; /* dummy - not required */
+        int binaryTime; /* size: either 4 or 6 */
+    } typeSpec;
+};
+
+
+#endif /* MMS_TYPES_H_ */

+ 1075 - 0
library/include/libiec61850/mms_value.h

@@ -0,0 +1,1075 @@
+/*
+ *  mms_value.h
+ *
+ *  Copyright 2013-2018 Michael Zillgith
+ *
+ *  This file is part of libIEC61850.
+ *
+ *  libIEC61850 is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  libIEC61850 is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with libIEC61850.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef MMS_VALUE_H_
+#define MMS_VALUE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "libiec61850_common_api.h"
+#include "mms_common.h"
+#include "mms_types.h"
+
+/**
+ * \defgroup common_api_group libIEC61850 API common parts
+ */
+/**@{*/
+
+/**
+ * \defgroup MMS_VALUE MmsValue data type definition and handling functions
+ */
+/**@{*/
+
+
+typedef enum {
+    DATA_ACCESS_ERROR_SUCCESS_NO_UPDATE = -3,
+    DATA_ACCESS_ERROR_NO_RESPONSE = -2, /* for server internal purposes only! */
+    DATA_ACCESS_ERROR_SUCCESS = -1,
+    DATA_ACCESS_ERROR_OBJECT_INVALIDATED = 0,
+    DATA_ACCESS_ERROR_HARDWARE_FAULT = 1,
+    DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE = 2,
+    DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED = 3,
+    DATA_ACCESS_ERROR_OBJECT_UNDEFINED = 4,
+    DATA_ACCESS_ERROR_INVALID_ADDRESS = 5,
+    DATA_ACCESS_ERROR_TYPE_UNSUPPORTED = 6,
+    DATA_ACCESS_ERROR_TYPE_INCONSISTENT = 7,
+    DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT = 8,
+    DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED = 9,
+    DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT = 10,
+    DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID = 11,
+    DATA_ACCESS_ERROR_UNKNOWN = 12
+} MmsDataAccessError;
+
+/**
+ * MmsValue - complex value type for MMS Client API
+ */
+typedef struct sMmsValue MmsValue;
+
+/*************************************************************************************
+ *  Array functions
+ *************************************************************************************/
+
+/**
+ * \brief Create an Array and initialize elements with default values.
+ *
+ * \param elementType type description for the elements the new array
+ * \param size the size of the new array
+ *
+ * \return a newly created array instance
+ */
+LIB61850_API MmsValue*
+MmsValue_createArray(const MmsVariableSpecification* elementType, int size);
+
+/**
+ * \brief Get the size of an array.
+ *
+ * \param self MmsValue instance to operate on. Has to be of type MMS_ARRAY.
+ *
+ * \return the size of the array
+ */
+LIB61850_API uint32_t
+MmsValue_getArraySize(const MmsValue* self);
+
+/**
+ * \brief Get an element of an array or structure.
+ *
+ * \param self MmsValue instance to operate on. Has to be of type MMS_ARRAY or MMS_STRUCTURE.
+ * \param index ndex of the requested array or structure element
+ *
+ * \return the element object
+ */
+LIB61850_API MmsValue*
+MmsValue_getElement(const MmsValue* array, int index);
+
+/**
+ * \brief Create an emtpy array.
+ *
+ * \param size the size of the new array
+ *
+ * \return a newly created empty array instance
+ */
+LIB61850_API MmsValue*
+MmsValue_createEmptyArray(int size);
+
+/**
+ * \brief Set an element of a complex type
+ *
+ * NOTE: If the element already exists it will simply be replaced by the provided new value.
+ * The caller is responsible to free the replaced value.
+ *
+ * \param complexValue MmsValue instance to operate on. Has to be of a type MMS_STRUCTURE or MMS_ARRAY
+ * \param the index of the element to set/replace
+ * \param elementValue the (new) value of the element
+ */
+LIB61850_API void
+MmsValue_setElement(MmsValue* complexValue, int index, MmsValue* elementValue);
+
+
+/*************************************************************************************
+ * Basic type functions
+ *************************************************************************************/
+
+LIB61850_API MmsDataAccessError
+MmsValue_getDataAccessError(const MmsValue* self);
+
+/**
+ * \brief Get the int64_t value of a MmsValue object.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_INTEGER or MMS_UNSIGNED
+ *
+ * \return signed 64 bit integer
+ */
+LIB61850_API int64_t
+MmsValue_toInt64(const MmsValue* self);
+
+/**
+ * \brief Get the int32_t value of a MmsValue object.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_INTEGER or MMS_UNSIGNED
+ *
+ * \return signed 32 bit integer
+ */
+LIB61850_API int32_t
+MmsValue_toInt32(const MmsValue* value);
+
+/**
+ * \brief Get the uint32_t value of a MmsValue object.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_INTEGER or MMS_UNSIGNED
+ *
+ * \return unsigned 32 bit integer
+ */
+LIB61850_API uint32_t
+MmsValue_toUint32(const MmsValue* value);
+
+/**
+ * \brief Get the double value of a MmsValue object.
+ *
+ * \param self MmsValue instance to operate on. Has to be of type MMS_FLOAT.
+ *
+ * \return 64 bit floating point value
+ */
+LIB61850_API double
+MmsValue_toDouble(const MmsValue* self);
+
+/**
+ * \brief Get the float value of a MmsValue object.
+ *
+ * \param self MmsValue instance to operate on. Has to be of type MMS_FLOAT.
+ *
+ * \return 32 bit floating point value
+ */
+LIB61850_API float
+MmsValue_toFloat(const MmsValue* self);
+
+/**
+ * \brief Get the unix timestamp of a MmsValue object of type MMS_UTCTIME.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_UTC_TIME.
+ *
+ * \return unix timestamp of the MMS_UTCTIME variable.
+ */
+LIB61850_API uint32_t
+MmsValue_toUnixTimestamp(const MmsValue* self);
+
+/**
+ * \brief Set the float value of a MmsValue object.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_FLOAT.
+ */
+LIB61850_API void
+MmsValue_setFloat(MmsValue* self, float newFloatValue);
+
+/**
+ * \brief Set the double value of a MmsValue object.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_FLOAT.
+ */
+LIB61850_API void
+MmsValue_setDouble(MmsValue* self, double newFloatValue);
+
+/**
+ * \brief Set the Int8 value of a MmsValue object.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_INTEGER.
+ * \param integer the new value to set
+ */
+LIB61850_API void
+MmsValue_setInt8(MmsValue* value, int8_t integer);
+
+/**
+ * \brief Set the Int16 value of a MmsValue object.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_INTEGER.
+ * \param integer the new value to set
+ */
+LIB61850_API void
+MmsValue_setInt16(MmsValue* value, int16_t integer);
+
+/**
+ * \brief Set the Int32 value of a MmsValue object.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_INTEGER.
+ * \param integer the new value to set
+ */
+LIB61850_API void
+MmsValue_setInt32(MmsValue* self, int32_t integer);
+
+/**
+ * \brief Set the Int64 value of a MmsValue object.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_INTEGER.
+ * \param integer the new value to set
+ */
+LIB61850_API void
+MmsValue_setInt64(MmsValue* value, int64_t integer);
+
+/**
+ * \brief Set the UInt8 value of a MmsValue object.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_UNSIGNED.
+ * \param integer the new value to set
+ */
+LIB61850_API void
+MmsValue_setUint8(MmsValue* value, uint8_t integer);
+
+/**
+ * \brief Set the UInt16 value of a MmsValue object.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_UNSIGNED.
+ * \param integer the new value to set
+ */
+LIB61850_API void
+MmsValue_setUint16(MmsValue* value, uint16_t integer);
+
+/**
+ * \brief Set the UInt32 value of a MmsValue object.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_UNSIGNED.
+ * \param integer the new value to set
+ */
+LIB61850_API void
+MmsValue_setUint32(MmsValue* value, uint32_t integer);
+
+
+/**
+ * \brief Set the bool value of a MmsValue object.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_BOOLEAN.
+ * \param boolValue a bool value
+ */
+LIB61850_API void
+MmsValue_setBoolean(MmsValue* value, bool boolValue);
+
+/**
+ * \brief Get the bool value of a MmsValue object.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_BOOLEAN.
+ * \return  the MmsValue value as bool value
+ */
+LIB61850_API bool
+MmsValue_getBoolean(const MmsValue* value);
+
+/**
+ * \brief Returns the value of an MMS_VISIBLE_STRING object as C string
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_VISIBLE_STRING or MMS_STRING.
+ *
+ * \returns the string value as 0 terminated C string
+ */
+LIB61850_API const char*
+MmsValue_toString(MmsValue* self);
+
+/**
+ * \brief Returns the (maximum) length of the string
+ *
+ * NOTE: this function return the amount of memory allocated for the string buffer - 1.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_VISIBLE_STRING or MMS_STRING.
+ */
+LIB61850_API int
+MmsValue_getStringSize(MmsValue* self);
+
+LIB61850_API void
+MmsValue_setVisibleString(MmsValue* self, const char* string);
+
+
+/**
+ * \brief Set a single bit (set to one) of an MmsType object of type MMS_BITSTRING
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_BITSTRING.
+ * \param bitPos the position of the bit in the bit string. Starting with 0. The bit
+ *        with position 0 is the first bit if the MmsValue instance is serialized.
+ * \param value the new value of the bit (true = 1 / false = 0)
+ */
+LIB61850_API void
+MmsValue_setBitStringBit(MmsValue* self, int bitPos, bool value);
+
+/**
+ * \brief Get the value of a single bit (set to one) of an MmsType object of type MMS_BITSTRING
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_BITSTRING.
+ * \param bitPos the position of the bit in the bit string. Starting with 0. The bit
+ *        with position 0 is the first bit if the MmsValue instance is serialized.
+ * \return the value of the bit (true = 1 / false = 0)
+ */
+LIB61850_API bool
+MmsValue_getBitStringBit(const MmsValue* self, int bitPos);
+
+/**
+ * \brief Delete all bits (set to zero) of an MmsType object of type MMS_BITSTRING
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_BITSTRING.
+ */
+LIB61850_API void
+MmsValue_deleteAllBitStringBits(MmsValue* self);
+
+
+/**
+ * \brief Get the size of a bit string in bits.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_BITSTRING.
+ */
+LIB61850_API int
+MmsValue_getBitStringSize(const MmsValue* self);
+
+/**
+ * \brief Get the number of bytes required by this bitString
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_BITSTRING.
+ */
+LIB61850_API int
+MmsValue_getBitStringByteSize(const MmsValue* self);
+
+/**
+ * \brief Count the number of set bits in a bit string.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_BITSTRING.
+ */
+LIB61850_API int
+MmsValue_getNumberOfSetBits(const MmsValue* self);
+
+/**
+ * Set all bits (set to one) of an MmsType object of type MMS_BITSTRING
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_BITSTRING.
+ */
+LIB61850_API void
+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.
+ */
+LIB61850_API uint32_t
+MmsValue_getBitStringAsInteger(const 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
+ */
+LIB61850_API 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.
+ */
+LIB61850_API uint32_t
+MmsValue_getBitStringAsIntegerBigEndian(const 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
+ */
+LIB61850_API void
+MmsValue_setBitStringFromIntegerBigEndian(MmsValue* self, uint32_t intValue);
+
+/**
+ * \brief Update an MmsValue object of UtcTime type with a timestamp in s
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_BOOLEAN.
+ * \param timeval the new value in seconds since epoch (1970/01/01 00:00 UTC)
+ */
+LIB61850_API MmsValue*
+MmsValue_setUtcTime(MmsValue* self, uint32_t timeval);
+
+/**
+ * \brief Update an MmsValue object of type MMS_UTCTIME with a millisecond time.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_UTCTIME.
+ * \param timeval the new value in milliseconds since epoch (1970/01/01 00:00 UTC)
+ */
+LIB61850_API MmsValue*
+MmsValue_setUtcTimeMs(MmsValue* self, uint64_t timeval);
+
+/**
+ * \brief Update an MmsValue object of type MMS_UTCTIME with a buffer containing a BER encoded UTCTime.
+ *
+ * The buffer must have a size of 8 bytes!
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_UTCTIME.
+ * \param buffer buffer containing the encoded UTCTime.
+ */
+LIB61850_API void
+MmsValue_setUtcTimeByBuffer(MmsValue* self, const uint8_t* buffer);
+
+/**
+ * \brief return the raw buffer containing the UTC time data
+ *
+ * Note: This will return the address of the raw byte buffer. The array length is 8 byte.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_UTCTIME.
+ *
+ * \return the buffer containing the raw data
+ */
+LIB61850_API uint8_t*
+MmsValue_getUtcTimeBuffer(MmsValue* self);
+
+/**
+ * \brief Get a millisecond time value from an MmsValue object of MMS_UTCTIME type.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_UTCTIME.
+ *
+ * \return the value in milliseconds since epoch (1970/01/01 00:00 UTC)
+ */
+LIB61850_API uint64_t
+MmsValue_getUtcTimeInMs(const MmsValue* value);
+
+/**
+ * \brief Get a millisecond time value and optional us part from an MmsValue object of MMS_UTCTIME type.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_UTCTIME.
+ * \param usec a pointer to store the us (microsecond) value.
+ *
+ * \return the value in milliseconds since epoch (1970/01/01 00:00 UTC)
+ */
+LIB61850_API uint64_t
+MmsValue_getUtcTimeInMsWithUs(const MmsValue* self, uint32_t* usec);
+
+/**
+ * \brief set the TimeQuality byte of the UtcTime
+ *
+ * Meaning of the bits in the timeQuality byte:
+ *
+ * bit 7 = leapSecondsKnown
+ * bit 6 = clockFailure
+ * bit 5 = clockNotSynchronized
+ * bit 0-4 = subsecond time accuracy (number of significant bits of subsecond time)
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_UTCTIME.
+ * \param timeQuality the byte representing the time quality
+ */
+LIB61850_API void
+MmsValue_setUtcTimeQuality(MmsValue* self, uint8_t timeQuality);
+
+/**
+ * \brief Update an MmsValue object of type MMS_UTCTIME with a millisecond time.
+ * 
+ * Meaning of the bits in the timeQuality byte:
+ *
+ * bit 7 = leapSecondsKnown
+ * bit 6 = clockFailure
+ * bit 5 = clockNotSynchronized
+ * bit 0-4 = subsecond time accuracy (number of significant bits of subsecond time)
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_UTCTIME.
+ * \param timeval the new value in milliseconds since epoch (1970/01/01 00:00 UTC)
+ * \param timeQuality the byte representing the time quality
+ * 
+ * \return the updated MmsValue instance
+ */
+LIB61850_API MmsValue*
+MmsValue_setUtcTimeMsEx(MmsValue* self, uint64_t timeval, uint8_t timeQuality);
+
+/**
+ * \brief get the TimeQuality byte of the UtcTime
+ *
+ * Meaning of the bits in the timeQuality byte:
+ *
+ * bit 7 = leapSecondsKnown
+ * bit 6 = clockFailure
+ * bit 5 = clockNotSynchronized
+ * bit 0-4 = subsecond time accuracy (number of significant bits of subsecond time)
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_UTCTIME.
+ *
+ * \return the byte representing the time quality
+ */
+LIB61850_API uint8_t
+MmsValue_getUtcTimeQuality(const MmsValue* self);
+
+/**
+ * \brief Update an MmsValue object of type MMS_BINARYTIME with a millisecond time.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_UTCTIME.
+ * \param timeval the new value in milliseconds since epoch (1970/01/01 00:00 UTC)
+ */
+LIB61850_API void
+MmsValue_setBinaryTime(MmsValue* self, uint64_t timestamp);
+
+/**
+ * \brief Get a millisecond time value from an MmsValue object of type MMS_BINARYTIME.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_BINARYTIME.
+ *
+ * \return the value in milliseconds since epoch (1970/01/01 00:00 UTC)
+ */
+LIB61850_API uint64_t
+MmsValue_getBinaryTimeAsUtcMs(const MmsValue* self);
+
+/**
+ * \brief Set the value of an MmsValue object of type MMS_OCTET_STRING.
+ *
+ * This method will copy the provided buffer to the internal buffer of the
+ * MmsValue instance. This will only happen if the internal buffer size is large
+ * enough for the new value. Otherwise the object value is not changed.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_OCTET_STRING.
+ * \param buf the buffer that contains the new value
+ * \param size the size of the buffer that contains the new value
+ */
+LIB61850_API void
+MmsValue_setOctetString(MmsValue* self, const uint8_t* buf, int size);
+
+/**
+ * \brief Set a single octet of an MmsValue object of type MMS_OCTET_STRING.
+ *
+ * This method will copy the provided octet to the internal buffer of the
+ * MmsValue instance, at the 'octetPos' position. This will only happen
+ * if the internal buffer size is large enough. Otherwise the object value is not changed.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_OCTET_STRING.
+ * \param octetPos the position of the octet in the octet string. Starting with 0.
+ *        The octet with position 0 is the first octet if the MmsValue instance is serialized.
+ * \param value the new value of the octet (0 to 255, or 0x00 to 0xFF)
+ */
+LIB61850_API void
+MmsValue_setOctetStringOctet(MmsValue* self, int octetPos, uint8_t value);
+
+/**
+ * \brief Returns the size in bytes of an MmsValue object of type MMS_OCTET_STRING.
+ *
+ * NOTE: To access the byte in the buffer the function \ref MmsValue_getOctetStringBuffer
+ * has to be used.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_OCTET_STRING.
+ *
+ * \return size in bytes
+ */
+LIB61850_API uint16_t
+MmsValue_getOctetStringSize(const MmsValue* self);
+
+/**
+ * \brief Returns the maximum size in bytes of an MmsValue object of type MMS_OCTET_STRING.
+ *
+ * Returns the maximum size if bytes of the MmsValue object of type MMS_OCTET_STRING. This
+ * is the size of the internally allocated buffer to hold the octet string data.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_OCTET_STRING.
+ *
+ * \return maximum size in bytes
+ */
+LIB61850_API uint16_t
+MmsValue_getOctetStringMaxSize(MmsValue* self);
+
+/**
+ * \brief Returns the reference to the internally hold buffer of an MmsValue object of type MMS_OCTET_STRING.
+ *
+ * NOTE: The size of the buffer can be requested with the \ref MmsValue_getOctetStringSize function.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_OCTET_STRING.
+ *
+ * \return reference to the buffer
+ */
+LIB61850_API uint8_t*
+MmsValue_getOctetStringBuffer(MmsValue* self);
+
+/**
+ * \brief Get the value of a single octet of an MmsType object of type MMS_OCTET_STRING
+ *
+ * NOTE: The octet quantity of the octet string can be requested with
+ * the \ref MmsValue_getOctetStringSize function.
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_OCTET_STRING.
+ * \param octetPos the position of the octet in the octet string. Starting with 0. The octet
+ *        with position 0 is the first octet if the MmsValue instance is serialized.
+ * \return the value of the octet (0 to 255, or 0x00 to 0xFF)
+ */
+LIB61850_API uint8_t
+MmsValue_getOctetStringOctet(MmsValue* self, int octetPos);
+
+/**
+ * \brief Update the value of an MmsValue instance by the value of another MmsValue instance.
+ *
+ * Both instances should be of same time. E.g. is self is of type MMS_INTEGER then
+ * source has also to be of type MMS_INTEGER. Otherwise the call will have no effect.
+ *
+ * \param self MmsValue instance to operate on.
+ * \param source MmsValue used as source for the update. Has to be of same type as self
+ *
+ * \return indicates if the update has been successful (false if not)
+ */
+LIB61850_API bool
+MmsValue_update(MmsValue* self, const MmsValue* source);
+
+/**
+ * \brief Check if two instances of MmsValue have the same value.
+ *
+ * Both instances should be of same type. E.g. is self is of type MMS_INTEGER then
+ * source has also to be of type MMS_INTEGER. Otherwise the call will return false.
+ *
+ * \param self MmsValue instance to operate on.
+ * \param otherValue MmsValue that is used to test
+ *
+ * \return true if both instances are of the same type and have the same value
+ */
+LIB61850_API bool
+MmsValue_equals(const MmsValue* self, const MmsValue* otherValue);
+
+/**
+ * \brief Check if two (complex) instances of MmsValue have the same type.
+ *
+ * This function will test if the two values are of the same type. The function
+ * will return true only if all child instances in the MmsValue instance tree are
+ * also of equal type.
+ *
+ * \param self MmsValue instance to operate on.
+ * \param otherValue MmsValue that is used to test
+ *
+ * \return true if both instances and all their children are of the same type.
+ */
+LIB61850_API bool
+MmsValue_equalTypes(const MmsValue* self, const MmsValue* otherValue);
+
+/*************************************************************************************
+ * Constructors and destructors
+ *************************************************************************************/
+
+
+LIB61850_API MmsValue*
+MmsValue_newDataAccessError(MmsDataAccessError accessError);
+
+LIB61850_API MmsValue*
+MmsValue_newInteger(int size);
+
+LIB61850_API MmsValue*
+MmsValue_newUnsigned(int size);
+
+LIB61850_API MmsValue*
+MmsValue_newBoolean(bool boolean);
+
+/**
+ * \brief Create a new MmsValue instance of type MMS_BITSTRING.
+ *
+ * \param bitSize the size of the bit string in bit
+ *
+ * \return new MmsValue instance of type MMS_BITSTRING
+ */
+LIB61850_API MmsValue*
+MmsValue_newBitString(int bitSize);
+
+LIB61850_API MmsValue*
+MmsValue_newOctetString(int size, int maxSize);
+
+LIB61850_API MmsValue*
+MmsValue_newStructure(const MmsVariableSpecification* typeSpec);
+
+LIB61850_API MmsValue*
+MmsValue_createEmptyStructure(int size);
+
+LIB61850_API MmsValue*
+MmsValue_newDefaultValue(const MmsVariableSpecification* typeSpec);
+
+LIB61850_API MmsValue*
+MmsValue_newIntegerFromInt8(int8_t integer);
+
+LIB61850_API MmsValue*
+MmsValue_newIntegerFromInt16(int16_t integer);
+
+LIB61850_API MmsValue*
+MmsValue_newIntegerFromInt32(int32_t integer);
+
+LIB61850_API MmsValue*
+MmsValue_newIntegerFromInt64(int64_t integer);
+
+LIB61850_API MmsValue*
+MmsValue_newUnsignedFromUint32(uint32_t integer);
+
+/**
+ * \brief Create a new 32 bit wide float variable and initialize with value
+ *
+ * \param value the initial value
+ *
+ * \return new MmsValue instance of type MMS_FLOAT
+ */
+LIB61850_API MmsValue*
+MmsValue_newFloat(float value);
+
+/**
+ * \brief Create a new 64 bit wide float variable and initialize with value
+ *
+ * \param value the initial value
+ *
+ * \return new MmsValue instance of type MMS_FLOAT
+ */
+LIB61850_API MmsValue*
+MmsValue_newDouble(double value);
+
+/**
+ * \brief Create a (deep) copy of an MmsValue instance
+ *
+ * This operation will allocate dynamic memory. It is up to the caller to
+ * free this memory by calling MmsValue_delete() later.
+ *
+ * \param self the MmsValue instance that will be cloned
+ *
+ * \return an MmsValue instance that is an exact copy of the given instance.
+ */
+LIB61850_API MmsValue*
+MmsValue_clone(const MmsValue* self);
+
+/**
+ * \brief Create a (deep) copy of an MmsValue instance in a user provided buffer
+ *
+ * This operation copies the give MmsValue instance to a user provided buffer.
+ *
+ * \param self the MmsValue instance that will be cloned
+ * \param destinationAddress the start address of the user provided buffer
+ *
+ * \return a pointer to the position in the buffer just after the last byte written.
+ */
+LIB61850_API uint8_t*
+MmsValue_cloneToBuffer(const MmsValue* self, uint8_t* destinationAddress);
+
+/**
+ * \brief Determine the required amount of bytes by a clone.
+ *
+ * This function is intended to be used to determine the buffer size of a clone operation
+ * (MmsValue_cloneToBuffer) in advance.
+ *
+ * \param self the MmsValue instance
+ *
+ * \return the number of bytes required by a clone
+ */
+LIB61850_API int
+MmsValue_getSizeInMemory(const MmsValue* self);
+
+/**
+ * \brief Delete an MmsValue instance.
+ *
+ * This operation frees all dynamically allocated memory of the MmsValue instance.
+ * If the instance is of type MMS_STRUCTURE or MMS_ARRAY all child elements will
+ * be deleted too.
+ *
+ * \param self the MmsValue instance to be deleted.
+ */
+LIB61850_API void
+MmsValue_delete(MmsValue* self);
+
+/**
+ * \brief Delete an MmsValue instance.
+ *
+ * This operation frees all dynamically allocated memory of the MmsValue instance.
+ * If the instance is of type MMS_STRUCTURE or MMS_ARRAY all child elements will
+ * be deleted too.
+ *
+ * NOTE: this functions only frees the instance if deleteValue field = 0!
+ *
+ *
+ * \param self the MmsValue instance to be deleted.
+ */
+LIB61850_API void
+MmsValue_deleteConditional(MmsValue* value);
+
+/**
+ * \brief Create a new MmsValue instance of type MMS_VISIBLE_STRING.
+ *
+ * This function will allocate as much memory as required to hold the string and sets the maximum size of
+ * the string to this size.
+ *
+ * \param string a text string that should be the value of the new instance of NULL for an empty string.
+ *
+ * \return new MmsValue instance of type MMS_VISIBLE_STRING
+ */
+LIB61850_API MmsValue*
+MmsValue_newVisibleString(const char* string);
+
+/**
+ * \brief Create a new MmsValue instance of type MMS_VISIBLE_STRING.
+ *
+ * This function will create a new empty MmsValue string object. The maximum size of the string is set
+ * according to the size parameter. The function allocates as much memory as is required to hold a string
+ * of the maximum size.
+ *
+ * \param size the new maximum size of the string.
+ *
+ * \return new MmsValue instance of type MMS_VISIBLE_STRING
+ */
+LIB61850_API MmsValue*
+MmsValue_newVisibleStringWithSize(int size);
+
+/**
+ * \brief Create a new MmsValue instance of type MMS_STRING.
+ *
+ * This function will create a new empty MmsValue string object. The maximum size of the string is set
+ * according to the size parameter. The function allocates as much memory as is required to hold a string
+ * of the maximum size.
+ *
+ * \param size the new maximum size of the string.
+ *
+ * \return new MmsValue instance of type MMS_STRING
+ */
+LIB61850_API MmsValue*
+MmsValue_newMmsStringWithSize(int size);
+
+/**
+ * \brief Create a new MmsValue instance of type MMS_BINARYTIME.
+ *
+ * If the timeOfDay parameter is set to true then the resulting
+ * MMS_BINARYTIME object is only 4 octets long and includes only
+ * the seconds since midnight. Otherwise the MMS_BINARYTIME
+ *
+ * \param timeOfDay if true only the TimeOfDay value is included.
+ *
+ * \return new MmsValue instance of type MMS_BINARYTIME
+ */
+LIB61850_API MmsValue*
+MmsValue_newBinaryTime(bool timeOfDay);
+
+/**
+ * \brief Create a new MmsValue instance of type MMS_VISIBLE_STRING from the specified byte array
+ *
+ * \param byteArray the byte array containing the string data
+ * \param size the size of the byte array
+ *
+ * \return new MmsValue instance of type MMS_VISIBLE_STRING
+ */
+LIB61850_API MmsValue*
+MmsValue_newVisibleStringFromByteArray(const uint8_t* byteArray, int size);
+
+/**
+ * \brief Create a new MmsValue instance of type MMS_STRING from the specified byte array
+ *
+ * \param byteArray the byte array containing the string data
+ * \param size the size of the byte array
+ *
+ * \return new MmsValue instance of type MMS_STRING
+ */
+LIB61850_API MmsValue*
+MmsValue_newMmsStringFromByteArray(const uint8_t* byteArray, int size);
+
+/**
+ * \brief Create a new MmsValue instance of type MMS_STRING.
+ *
+ * \param string a text string that should be the value of the new instance of NULL for an empty string.
+ *
+ * \return new MmsValue instance of type MMS_STRING
+ */
+LIB61850_API MmsValue*
+MmsValue_newMmsString(const char* string);
+
+/**
+ * \brief Set the value of MmsValue instance of type MMS_STRING
+ *
+ * \param string a text string that will be the new value of the instance
+ */
+LIB61850_API void
+MmsValue_setMmsString(MmsValue* value, const char* string);
+
+/**
+ * \brief Create a new MmsValue instance of type MMS_UTCTIME.
+ *
+ * \param timeval time value as UNIX timestamp (seconds since epoch)
+ *
+ * \return new MmsValue instance of type MMS_UTCTIME
+ */
+LIB61850_API MmsValue*
+MmsValue_newUtcTime(uint32_t timeval);
+
+/**
+ * \brief Create a new MmsValue instance of type MMS_UTCTIME.
+ *
+ * \param timeval time value as millisecond timestamp (milliseconds since epoch)
+ *
+ * \return new MmsValue instance of type MMS_UTCTIME
+ */
+LIB61850_API MmsValue*
+MmsValue_newUtcTimeByMsTime(uint64_t timeval);
+
+
+LIB61850_API void
+MmsValue_setDeletable(MmsValue* self);
+
+LIB61850_API void
+MmsValue_setDeletableRecursive(MmsValue* value);
+
+/**
+ * \brief Check if the MmsValue instance has the deletable flag set.
+ *
+ * The deletable flag indicates if an libiec61850 API client should call the
+ * MmsValue_delete() method or not if the MmsValue instance was passed to the
+ * client by an API call or callback method.
+ *
+ * \param self the MmsValue instance
+ *
+ * \return 1 if deletable flag is set, otherwise 0
+ */
+LIB61850_API int
+MmsValue_isDeletable(MmsValue* self);
+
+/**
+ * \brief Get the MmsType of an MmsValue instance
+ *
+ * \param self the MmsValue instance
+ */
+LIB61850_API MmsType
+MmsValue_getType(const MmsValue* self);
+
+/**
+ * \brief Get a sub-element of a MMS_STRUCTURE value specified by a path name.
+ *
+ * \param self the MmsValue instance
+ * \param varSpec - type specification if the MMS_STRUCTURE value
+ * \param mmsPath - path (in MMS variable name syntax) to specify the sub element.
+ *
+ * \return the sub elements MmsValue instance or NULL if the element does not exist
+ */
+LIB61850_API MmsValue*
+MmsValue_getSubElement(MmsValue* self, MmsVariableSpecification* varSpec, char* mmsPath);
+
+/**
+ * \brief return the value type as a human readable string
+ *
+ * \param self the MmsValue instance
+ *
+ * \return the value type as a human readable string
+ */
+LIB61850_API char*
+MmsValue_getTypeString(MmsValue* self);
+
+/**
+ * \brief create a string representation of the MmsValue object in the provided buffer
+ *
+ * NOTE: This function is for debugging purposes only. It may not be aimed to be used
+ * in embedded systems. It requires a full featured snprintf function.
+ *
+ * \param self the MmsValue instance
+ * \param buffer the buffer where to copy the string representation
+ * \param bufferSize the size of the provided buffer
+ *
+ * \return a pointer to the start of the buffer
+ */
+LIB61850_API const char*
+MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize);
+
+/**
+ * \brief create a new MmsValue instance from a BER encoded MMS Data element (deserialize)
+ *
+ * WARNING: API changed with version 1.0.3 (added endBufPos parameter)
+ *
+ * \param buffer the buffer to read from
+ * \param bufPos the start position of the mms value data in the buffer
+ * \param bufferLength the length of the buffer
+ * \param endBufPos the position in the buffer after the read MMS data element (NULL if not required)
+ *
+ * \return the MmsValue instance created from the buffer
+ */
+LIB61850_API MmsValue*
+MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength, int* endBufPos);
+
+/**
+ * \brief create a new MmsValue instance from a BER encoded MMS Data element (deserialize) with a defined maximum recursion depth
+ *
+ * \param buffer the buffer to read from
+ * \param bufPos the start position of the mms value data in the buffer
+ * \param bufferLength the length of the buffer
+ * \param endBufPos the position in the buffer after the read MMS data element (NULL if not required)
+ * \param maxDepth the maximum recursion depth
+ *
+ * \return the MmsValue instance created from the buffer
+ */
+LIB61850_API MmsValue*
+MmsValue_decodeMmsDataMaxRecursion(uint8_t* buffer, int bufPos, int bufferLength, int* endBufPos, int maxDepth);
+
+/**
+ * \brief Serialize the MmsValue instance as BER encoded MMS Data element
+ *
+ * \param self the MmsValue instance
+ *
+ * \param buffer the buffer to encode the MMS data element
+ * \param bufPos the position in the buffer where to start encoding
+ * \param encode encode to buffer (true) or calculate length only (false)
+ *
+ * \return the encoded length of the corresponding MMS data element
+ */
+LIB61850_API int
+MmsValue_encodeMmsData(MmsValue* self, uint8_t* buffer, int bufPos, bool encode);
+
+/**
+ * \brief Get the maximum possible BER encoded size of the MMS data element
+ *
+ * \param self the MmsValue instance
+ *
+ * \return the maximum encoded size in bytes of the MMS data element
+ */
+LIB61850_API int
+MmsValue_getMaxEncodedSize(MmsValue* self);
+
+/**
+ * \brief Calculate the maximum encoded size of a variable of this type
+ *
+ * \param self the MMS variable specification instance
+ */
+LIB61850_API int
+MmsVariableSpecification_getMaxEncodedSize(MmsVariableSpecification* self);
+
+/**@}*/
+
+/**@}*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MMS_VALUE_H_ */

+ 244 - 0
library/include/libiec61850/r_session.h

@@ -0,0 +1,244 @@
+/*
+ *  r_session.h
+ *
+ *  Copyright 2013-2022 Michael Zillgith
+ *
+ *  This file is part of libIEC61850.
+ *
+ *  libIEC61850 is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  libIEC61850 is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with libIEC61850.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef LIBIEC61850_R_SESSION_H_
+#define LIBIEC61850_R_SESSION_H_
+
+#include "libiec61850_common_api.h"
+#include "hal_socket.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct sRSession* RSession;
+
+typedef enum {
+    R_SESSION_SEC_ALGO_NONE = 0,
+    R_SESSION_SEC_ALGO_AES_128_GCM = 1,
+    R_SESSION_SEC_ALGO_AES_256_GCM = 2
+} RSecurityAlgorithm;
+
+typedef enum {
+    R_SESSION_SIG_ALGO_NONE = 0,
+    R_SESSION_SIG_ALGO_HMAC_SHA256_80 = 1,
+    R_SESSION_SIG_ALGO_HMAC_SHA256_128 = 2,
+    R_SESSION_SIG_ALGO_HMAC_SHA256_256 = 3,
+    R_SESSION_SIG_ALGO_AES_GMAC_64 = 4,
+    R_SESSION_SIG_ALGO_AES_GMAC_128 = 5,
+    R_SESSION_SIG_ALGO_HMAC_SHA3_80 = 6,
+    R_SESSION_SIG_ALGO_HMAC_SHA3_128 = 7,
+    R_SESSION_SIG_ALGO_HMAC_SHA3_256 = 8
+} RSignatureAlgorithm;
+
+typedef enum {
+    R_SESSION_ERROR_OK = 0,
+    R_SESSION_ERROR_INVALID_KEY = 1,
+    R_SESSION_ERROR_KEY_QUEUE_FULL = 2,
+    R_SESSION_ERROR_NO_SOCKET = 3,
+    R_SESSION_ERROR_OUT_OF_MEMORY = 4,
+    R_SESSION_ERROR_FAILED_TO_SEND = 5,
+    R_SESSION_ERROR_FAILED_TO_RECEIVE = 6,
+    R_SESSION_ERROR_INVALID_MESSAGE = 7,
+    R_SESSION_ERROR_SET_FAILED = 8
+} RSessionError;
+
+typedef struct sRSessionPayloadElement* RSessionPayloadElement;
+
+struct sRSessionPayloadElement
+{
+    bool simulation;
+    uint16_t appId;
+    uint8_t payloadType;
+    uint8_t* payload;
+    int payloadSize;
+    RSessionPayloadElement nextElement; /* NULL when no more elements follow */
+};
+
+/**
+ * \brief Create a new RSession instance to provide R-GOOSE/R-SMV support for GOOSE/SMV publisher/subscriber
+ *
+ * \return new RSession instance
+ */
+LIB61850_API RSession
+RSession_create(void);
+
+/**
+ * \brief Set the maximum buffer size for session messages (range: 128 - 65535)
+ *
+ * \param self the RSession instance
+ * \param bufferSize the size of the buffer for RSession UDP messages (range: 128 - 65535)
+ */
+LIB61850_API void
+RSession_setBufferSize(RSession self, uint16_t bufferSize);
+
+/**
+ * \brief Set the security algorithms for the session instance
+ * 
+ * \note only for version 1 of the protocol!
+ * 
+ * \param secAlgo encryption algorithm to be used for the session instance
+ * \param sigAlgo signature algorithm to be used for the session instance
+ * 
+ * \return returns R_SESSION_ERROR_OK
+ */
+LIB61850_API RSessionError
+RSession_setSecurity(RSession self, RSecurityAlgorithm secAlgo, RSignatureAlgorithm sigAlgo);
+
+/**
+ * \brief Bind the RSession instance to a specific local IP address and UDP port
+ * 
+ * \param self the RSession instance
+ * \param localAddress the local IP address to use
+ * \param localPort the local UDP port to use (default is 102)
+*/
+LIB61850_API RSessionError
+RSession_setLocalAddress(RSession self, const char* localAddress, int localPort);
+
+/**
+ * \brief Add this instance to an IPv4/IPv6 multicast group
+ *
+ * \param self the RSession instance
+ * \param multicastAddress IPv4 or IPv6 multicast address
+ *
+ * \return R_SESSION_ERROR_OK on success, R_SESSION_ERROR_SET_FAILED otherwise
+ */
+LIB61850_API RSessionError
+RSession_addMulticastGroup(RSession self, const char* multicastAddress);
+
+/**
+ * \brief Sets the multicast TTL (number of hops) for this RSession instance
+ *
+ * \param self the RSession instance
+ * \param ttl number of hops for multicast messages. Default is 1 (not routable!)
+ *
+ * \return R_SESSION_ERROR_OK on success, error code otherwise
+ */
+LIB61850_API RSessionError
+RSession_setMulticastTtl(RSession self, int ttl);
+
+/**
+ * \brief Set the destionation address and port for publishers
+ *
+ * \param self the RSession instance
+ * \param remoteAddress remote IP address
+ * \param remotePort remote UDP port
+ *
+ * \return R_SESSION_ERROR_OK on success, error code otherwise
+ */
+LIB61850_API RSessionError
+RSession_setRemoteAddress(RSession self, const char* remoteAddress, int remotePort);
+
+/**
+ * \brief Start sending and receiving messages (bind to a local UDP port/interface)
+ *
+ * \param self the RSession instance
+ *
+ * \return R_SESSION_ERROR_OK on success, error code otherwise
+ */
+LIB61850_API RSessionError
+RSession_start(RSession self);
+
+/**
+ * \brief Stop sending and receiving messages.
+ *
+ * \param self the RSession instance
+ *
+ * \return R_SESSION_ERROR_OK on success, error code otherwise
+ */
+LIB61850_API RSessionError
+RSession_stop(RSession self);
+
+/**
+ * \brief Manually add a key to the RSession instance
+ *
+ * \param self the RSession instance
+ * \param keyId the key ID is unique for the security association
+ * \param key the key data
+ * \param keyLength the length of the key in bytes
+ * \param secAlgo the applicable security (encryption) algorithm
+ * \param sigAlgo the applicable signature algorithm
+ */
+LIB61850_API RSessionError
+RSession_addKey(RSession self, uint32_t keyId, uint8_t* key, int keyLength, RSecurityAlgorithm secAlgo, RSignatureAlgorithm sigAlgo);
+
+/**
+ * \brief Remove key from the list of accepted keys
+ * 
+ * \param self the RSession instance
+ * \param keyId the key ID is unique for the security association
+ */
+LIB61850_API RSessionError
+RSession_removeKey(RSession self, uint32_t keyId);
+
+/**
+ * \brief Remove all keys from the list of accepted keys
+ * 
+ * \param self the RSession instance 
+ */
+void
+RSession_removeAllKeys(RSession self);
+
+typedef enum
+{
+    RSESSION_KEY_EVENT__NEED_KEY = 1
+} RSessionKeyEvent;
+
+typedef void (*RSession_KeyEventHandler) (void* parameter, RSession rSession, RSessionKeyEvent event, uint32_t keyID);
+
+/**
+ * \brief Set a callback handler to receive key events from the RSession instance
+ *
+ * e.g. when the RSession instance has no valid key for the received messages or to publish messages.
+ * 
+ * \param self the RSession instance
+ * \param handler the callback that is called when a new event happens
+ * \param parameter user provided parameter that is passed to the user callback
+ */
+LIB61850_API void
+RSession_setKeyEventHandler(RSession self, RSession_KeyEventHandler handler, void* parameter);
+
+/**
+ * \brief Set the active key for the sender/publisher
+ *
+ * \param self the RSession instance
+ * \param keyId the key ID of the new active key (has to be added with \ref RSession_addKey before).
+ * 
+ * \return R_SESSION_ERROR_INVALID_KEY when no valid key with the given keyId is avialable, R_SESSION_ERROR_OK otherwise
+ */
+LIB61850_API RSessionError
+RSession_setActiveKey(RSession self, uint32_t keyId);
+
+/**
+ * \brief Destroy the RSession instance and free all resource
+ *
+ * \param self the RSession instance
+ */
+LIB61850_API void
+RSession_destroy(RSession self);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBIEC61850_R_SESSION_H_ */

+ 73 - 0
library/include/libiec61850/sntp_client.h

@@ -0,0 +1,73 @@
+/*
+ *  Copyright 2013-2022 Michael Zillgith
+ *
+ *  This file is part of libIEC61850.
+ *
+ *  libIEC61850 is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  libIEC61850 is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with libIEC61850.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef LIBIEC61850_SRC_COMMON_INC_SNTP_CLIENT_H_
+#define LIBIEC61850_SRC_COMMON_INC_SNTP_CLIENT_H_
+
+#include "libiec61850_common_api.h"
+#include "hal_socket.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct sSNTPClient* SNTPClient;
+
+typedef void (*SNTPClient_UserCallback)(void* parameter, bool isSynced);
+
+LIB61850_API SNTPClient
+SNTPClient_create();
+
+LIB61850_API void
+SNTPClient_setLocalAddress(SNTPClient self, const char* localAddress);
+
+LIB61850_API void
+SNTPClient_setLocalPort(SNTPClient self, int udpPort);
+
+LIB61850_API HandleSet
+SNTPClient_getHandleSet(SNTPClient self);
+
+LIB61850_API void
+SNTPClient_addServer(SNTPClient self, const char* serverAddr, int serverPort);
+
+LIB61850_API void
+SNTPClient_setPollInterval(SNTPClient self, uint32_t intervalInSeconds);
+
+LIB61850_API void
+SNTPClient_setUserCallback(SNTPClient self, SNTPClient_UserCallback callback, void* parameter);
+
+LIB61850_API bool
+SNTPClient_isSynchronized(SNTPClient self);
+
+LIB61850_API void
+SNTPClient_start(SNTPClient self);
+
+LIB61850_API void
+SNTPClient_stop(SNTPClient self);
+
+LIB61850_API void
+SNTPClient_destroy(SNTPClient self);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBIEC61850_SRC_COMMON_INC_SNTP_CLIENT_H_ */

+ 510 - 0
library/include/libiec61850/sv_publisher.h

@@ -0,0 +1,510 @@
+/*
+ *  sv_publisher.h
+ *
+ *  Copyright 2016-2022 Michael Zillgith
+ *
+ *  This file is part of libIEC61850.
+ *
+ *  libIEC61850 is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  libIEC61850 is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with libIEC61850.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+
+#ifndef LIBIEC61850_SRC_SAMPLED_VALUES_SV_PUBLISHER_H_
+#define LIBIEC61850_SRC_SAMPLED_VALUES_SV_PUBLISHER_H_
+
+#include "iec61850_common.h"
+#include "r_session.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef GOOSE_SV_COMM_PARAMETERS
+#define GOOSE_SV_COMM_PARAMETERS
+
+typedef struct sCommParameters {
+    uint8_t vlanPriority;
+    uint16_t vlanId;
+    uint16_t appId;
+    uint8_t dstAddress[6];
+} CommParameters;
+
+#endif
+
+/**
+ * \defgroup sv_publisher_api_group IEC 61850 Sampled Values (SV) publisher API
+ */
+/**@{*/
+
+#define IEC61850_SV_SMPSYNC_NOT_SYNCHRONIZED 0
+#define IEC61850_SV_SMPSYNC_SYNCED_UNSPEC_LOCAL_CLOCK 1
+#define IEC61850_SV_SMPSYNC_SYNCED_GLOBAL_CLOCK 2
+
+#define IEC61850_SV_SMPMOD_PER_NOMINAL_PERIOD 0
+#define IEC61850_SV_SMPMOD_SAMPLES_PER_SECOND 1
+#define IEC61850_SV_SMPMOD_SECONDS_PER_SAMPLE 2
+
+/**
+ * \brief An opaque type representing an IEC 61850-9-2 Sampled Values publisher.
+ */
+typedef struct sSVPublisher* SVPublisher;
+
+/**
+ * \brief An opaque type representing an IEC 61850-9-2 Sampled Values Application Service Data Unit (ASDU).
+ */
+typedef struct sSVPublisher_ASDU* SVPublisher_ASDU;
+
+/**
+ * \brief Create a new IEC61850-9-2 Sampled Values publisher.
+ *
+ * NOTE: VLAN tagging is enabled when calling this constructor. To disable VLAN tagging
+ * use \ref SVPublisher_createEx instead.
+ *
+ * \param[in] interfaceId the name of the interface over which the SV publisher should send SV packets.
+ * \param[in] parameters optional parameters for setting VLAN options and destination MAC address. Use NULL for default values.
+ * \return the new SV publisher instance.
+ */
+LIB61850_API SVPublisher
+SVPublisher_create(CommParameters* parameters, const char* interfaceId);
+
+/**
+ * \brief Create a new IEC61850-9-2 Sampled Values publisher.
+ *
+ * \param[in] interfaceId the name of the interface over which the SV publisher should send SV packets.
+ * \param[in] parameters optional parameters for setting VLAN options and destination MAC address. Use NULL for default values.
+ * \param[in] useVlanTags enable(true)/disable(false) VLAN tagging
+ * \return the new SV publisher instance.
+ */
+LIB61850_API SVPublisher
+SVPublisher_createEx(CommParameters* parameters, const char* interfaceId, bool useVlanTag);
+
+/**
+ * \brief Create a new SVPublisher instance for R-SMV
+ *
+ * \param session R-session protocol instance to use
+ * \param appId the appID value to use
+ * 
+ * \return the new SVPublisher instance
+ */
+LIB61850_API SVPublisher
+SVPublisher_createRemote(RSession session, uint16_t appId);
+
+/**
+ * \brief Create an Application Service Data Unit (ASDU) and add it to an existing Sampled Values publisher.
+ *
+ * \param[in] svID
+ * \param[in] datset
+ * \param[in] confRev Configuration revision number. Should be incremented each time that the configuration of the logical device changes.
+ * \return the new ASDU instance.
+ */
+LIB61850_API SVPublisher_ASDU
+SVPublisher_addASDU(SVPublisher self, const char* svID, const char* datset, uint32_t confRev);
+
+/**
+ * \brief Prepare the publisher for publishing.
+ *
+ * This method must be called before SVPublisher_publish().
+ *
+ * \param[in] self the Sampled Values publisher instance.
+ */
+LIB61850_API void
+SVPublisher_setupComplete(SVPublisher self);
+
+/**
+ * \brief Publish all registered ASDUs
+ *
+ * \param[in] self the Sampled Values publisher instance.
+ */
+LIB61850_API void
+SVPublisher_publish(SVPublisher self);
+
+/**
+ * \brief Destroy an IEC61850-9-2 Sampled Values instance.
+ *
+ * \param[in] self the Sampled Values publisher instance.
+ */
+LIB61850_API void
+SVPublisher_destroy(SVPublisher self);
+
+/**
+ * \addtogroup sv_publisher_asdu_group Values Application Service Data Unit (ASDU)
+ *  @{
+ */
+
+/**
+ * \brief Reset the internal data buffer of an ASDU.
+ *
+ * All data elements added by SVPublisher_ASDU_add*() functions are removed.
+ * SVPublisher_setupComplete() must be called afterwards.
+ *
+ * \param[in] self the Sampled Values ASDU instance.
+ */
+LIB61850_API void
+SVPublisher_ASDU_resetBuffer(SVPublisher_ASDU self);
+
+/**
+ * \brief Reserve memory for a signed 8-bit integer in the ASDU.
+ *
+ * \param[in] self the Sampled Values ASDU instance.
+ * \return the offset in bytes within the ASDU data block.
+ */
+LIB61850_API int
+SVPublisher_ASDU_addINT8(SVPublisher_ASDU self);
+
+/**
+ * \brief Set the value of a 8-bit integer in the ASDU.
+ *
+ * \param[in] self the Sampled Values ASDU instance.
+ * \param[in] index The offset within the data block of the ASDU in bytes.
+ * \param[in] value The value which should be set.
+ */
+LIB61850_API void
+SVPublisher_ASDU_setINT8(SVPublisher_ASDU self, int index, int8_t value);
+
+/**
+ * \brief Reserve memory for a signed 32-bit integer in the ASDU.
+ *
+ * \param[in] self the Sampled Values ASDU instance.
+ * \return the offset in bytes within the ASDU data block.
+ */
+LIB61850_API int
+SVPublisher_ASDU_addINT32(SVPublisher_ASDU self);
+
+/**
+ * \brief Set the value of a 32-bit integer in the ASDU.
+ *
+ * \param[in] self the Sampled Values ASDU instance.
+ * \param[in] index The offset within the data block of the ASDU in bytes.
+ * \param[in] value The value which should be set.
+ */
+LIB61850_API void
+SVPublisher_ASDU_setINT32(SVPublisher_ASDU self, int index, int32_t value);
+
+/**
+ * \brief Reserve memory for a signed 64-bit integer in the ASDU.
+ *
+ * \param[in] self the Sampled Values ASDU instance.
+ * \return the offset in bytes of the new element within the ASDU data block.
+ */
+LIB61850_API int
+SVPublisher_ASDU_addINT64(SVPublisher_ASDU self);
+
+/**
+ * \brief Set the value of a 64-bit integer in the ASDU.
+ *
+ * \param[in] self the Sampled Values ASDU instance.
+ * \param[in] index The offset within the data block of the ASDU in bytes.
+ * \param[in] value The value which should be set.
+ */
+LIB61850_API void
+SVPublisher_ASDU_setINT64(SVPublisher_ASDU self, int index, int64_t value);
+
+/**
+ * \brief Reserve memory for a single precision floating point number in the ASDU.
+ *
+ * \param[in] self the Sampled Values ASDU instance.
+ * \return the offset in bytes of the new element within the ASDU data block.
+ */
+LIB61850_API int
+SVPublisher_ASDU_addFLOAT(SVPublisher_ASDU self);
+
+/**
+ * \brief Set the value of a single precision floating point number in the ASDU.
+ *
+ * \param[in] self the Sampled Values ASDU instance.
+ * \param[in] index The offset within the data block of the ASDU in bytes.
+ * \param[in] value The value which should be set.
+ */
+LIB61850_API void
+SVPublisher_ASDU_setFLOAT(SVPublisher_ASDU self, int index, float value);
+
+/**
+ * \brief Reserve memory for a double precision floating point number in the ASDU.
+ *
+ * \param[in] self the Sampled Values ASDU instance.
+ * \return the offset in bytes of the new element within the ASDU data block.
+ */
+LIB61850_API int
+SVPublisher_ASDU_addFLOAT64(SVPublisher_ASDU self);
+
+/**
+ * \brief Set the value of a double precision floating pointer number in the ASDU.
+ *
+ * \param[in] self the Sampled Values ASDU instance.
+ * \param[in] index The offset within the data block of the ASDU in bytes.
+ * \param[in] value The value which should be set.
+ */
+LIB61850_API void
+SVPublisher_ASDU_setFLOAT64(SVPublisher_ASDU self, int index, double value);
+
+/**
+ * \brief Reserve memory for a 64 bit time stamp in the ASDU
+ *
+ * \param[in] self the Sampled Values ASDU instance.
+ * \return the offset in bytes of the new element within the ASDU data block.
+ */
+LIB61850_API int
+SVPublisher_ASDU_addTimestamp(SVPublisher_ASDU self);
+
+/**
+ * \brief Set the value of a 64 bit time stamp in the ASDU.
+ *
+ * \param[in] self the Sampled Values ASDU instance.
+ * \param[in] index The offset within the data block of the ASDU in bytes.
+ * \param[in] value The value which should be set.
+ */
+LIB61850_API void
+SVPublisher_ASDU_setTimestamp(SVPublisher_ASDU self, int index, Timestamp value);
+
+/**
+ * \brief Reserve memory for a quality value in the ASDU
+ *
+ * NOTE: Quality is encoded as BITSTRING (4 byte)
+ *
+ * \param[in] self the Sampled Values ASDU instance.
+ * \return the offset in bytes of the new element within the ASDU data block.
+ */
+LIB61850_API int
+SVPublisher_ASDU_addQuality(SVPublisher_ASDU self);
+
+/**
+ * \brief Set the value of a quality attribute in the ASDU.
+ *
+ * \param[in] self the Sampled Values ASDU instance.
+ * \param[in] index The offset within the data block of the ASDU in bytes.
+ * \param[in] value The value which should be set.
+ */
+LIB61850_API void
+SVPublisher_ASDU_setQuality(SVPublisher_ASDU self, int index, Quality value);
+
+/**
+ * \brief Set the sample count attribute of the ASDU.
+ *
+ * \param[in] self the Sampled Values ASDU instance.
+ * \param[in] value the new value of the attribute.
+ */
+LIB61850_API void
+SVPublisher_ASDU_setSmpCnt(SVPublisher_ASDU self, uint16_t value);
+
+/**
+ * \brief Get the sample count attribute of the ASDU.
+ *
+ * \param[in] self the Sampled Values ASDU instance.
+ */
+LIB61850_API uint16_t
+SVPublisher_ASDU_getSmpCnt(SVPublisher_ASDU self);
+
+/**
+ * \brief Increment the sample count attribute of the ASDU.
+ *
+ * The parameter SmpCnt shall contain the values of a counter, which is incremented each time a new sample of the analogue value is taken.
+ * The sample values shall be kept in the right order.
+ * If the counter is used to indicate time consistency of various sampled values, the counter shall be reset by an external synchronization event.
+ *
+ * \param[in] self the Sampled Values ASDU instance.
+ */
+LIB61850_API void
+SVPublisher_ASDU_increaseSmpCnt(SVPublisher_ASDU self);
+
+/**
+ * \brief Set the roll-over (wrap) limit for the sample counter. When reaching the limit the
+ *        sample counter will be reset to 0 (default is no limit)
+ *
+ * \param[in] self the Sampled Values ASDU instance.
+ * \param[in] value the new sample counter limit
+ */
+LIB61850_API void
+SVPublisher_ASDU_setSmpCntWrap(SVPublisher_ASDU self, uint16_t value);
+
+/**
+ * \brief Enables the transmission of refresh time attribute of the ASDU
+ *
+ * The refresh time is the time when the data in put into the sampled values buffer
+ *
+ * \param[in] self the Sampled Values ASDU instance.
+ */
+LIB61850_API void
+SVPublisher_ASDU_enableRefrTm(SVPublisher_ASDU self);
+
+/**
+ * \brief Set the refresh time attribute of the ASDU with nanosecond resolution
+ *
+ * \param[in] self the Sampled Values ASDU instance.
+ * \param[in] refrTmNs the refresh time value with nanoseconds resolution.
+ */
+LIB61850_API void
+SVPublisher_ASDU_setRefrTmNs(SVPublisher_ASDU self, nsSinceEpoch refrTmNs);
+
+/**
+ * \brief Set the refresh time attribute of the ASDU with millisecond resolution
+ *
+ * \param[in] self the Sampled Values ASDU instance.
+ * \param[in] refrTmNs the refresh time value with with milliseconds resolution.
+ */
+LIB61850_API void
+SVPublisher_ASDU_setRefrTm(SVPublisher_ASDU self, msSinceEpoch refrTm);
+
+/**
+ * \brief Set the refresh time attribute of the ASDU
+ *
+ * NOTE: Using this function you can control the time quality flags and the
+ * sub second precision (number of valid bits) of the RefrTm value.
+ *
+ * \param[in] self the Sampled Values ASDU instance.
+ * \param[in] refrTm the refresh time value
+ */
+LIB61850_API void
+SVPublisher_ASDU_setRefrTmByTimestamp(SVPublisher_ASDU self, Timestamp* refrTm);
+
+/**
+ * \brief Set the sample mode attribute of the ASDU.
+ *
+ * The attribute SmpMod shall specify if the sample rate is defined in units of samples per nominal period, samples per second or seconds per sample.
+ * If it is missing, the default value is samples per period.
+ *
+ * NOTE: Function has to be called before calling \ref SVPublisher_setupComplete
+ *
+ * \param[in] self the Sampled Values ASDU instance.
+ * \param[in] smpMod one of IEC61850_SV_SMPMOD_PER_NOMINAL_PERIOD, IEC61850_SV_SMPMOD_SAMPLES_PER_SECOND or IEC61850_SV_SMPMOD_SECONDS_PER_SAMPLE
+ */
+LIB61850_API void
+SVPublisher_ASDU_setSmpMod(SVPublisher_ASDU self, uint8_t smpMod);
+
+/**
+ * \brief Set the sample rate attribute of the ASDU.
+ *
+ * The attribute SmpRate shall specify the sample rate.
+ * The value shall be interpreted depending on the value of the SmpMod attribute.
+ *
+ * NOTE: Function has to be called before calling \ref SVPublisher_setupComplete
+ *
+ * \param[in] self the Sampled Values ASDU instance.
+ * \param[in] smpRate Amount of samples (default per nominal period, see SmpMod).
+ */
+LIB61850_API void
+SVPublisher_ASDU_setSmpRate(SVPublisher_ASDU self, uint16_t smpRate);
+
+/**
+ * \brief Set the clock synchronization information
+ *
+ * Default value is not synchronized (0).
+ * Possible values are:
+ * 0 = SV are not synchronized by an external clock signal.
+ * 1 = SV are synchronized by a clock signal from an unspecified local area clock.
+ * 2 = SV are synchronized by a global area clock signal (time traceable).
+ * 5 to 254 = SV are synchronized by a clock signal from a local area clock identified by this value.
+ * 3;4;255 = Reserved values – Do not use.
+ *
+ * \param[in] self the Sampled Values ASDU instance.
+ * \param[in] smpSynch the clock synchronization state
+ */
+LIB61850_API void
+SVPublisher_ASDU_setSmpSynch(SVPublisher_ASDU self, uint16_t smpSynch);
+
+/**@} @}*/
+
+#ifndef DEPRECATED
+#if defined(__GNUC__) || defined(__clang__)
+  #define DEPRECATED __attribute__((deprecated))
+#else
+  #define DEPRECATED
+#endif
+#endif
+
+/**
+ * \addtogroup sv_publisher_deprecated_api_group Deprecated API
+ * \ingroup sv_publisher_api_group IEC 61850 Sampled Values (SV) publisher API
+ * \deprecated
+ * @{
+ */
+
+typedef struct sSVPublisher* SampledValuesPublisher;
+
+typedef struct sSV_ASDU* SV_ASDU;
+
+LIB61850_API DEPRECATED SVPublisher
+SampledValuesPublisher_create(CommParameters* parameters, const char* interfaceId);
+
+LIB61850_API DEPRECATED SVPublisher_ASDU
+SampledValuesPublisher_addASDU(SVPublisher self, char* svID, char* datset, uint32_t confRev);
+
+LIB61850_API DEPRECATED void
+SampledValuesPublisher_setupComplete(SVPublisher self);
+
+LIB61850_API DEPRECATED void
+SampledValuesPublisher_publish(SVPublisher self);
+
+LIB61850_API DEPRECATED void
+SampledValuesPublisher_destroy(SVPublisher self);
+
+LIB61850_API DEPRECATED void
+SV_ASDU_resetBuffer(SVPublisher_ASDU self);
+
+LIB61850_API DEPRECATED int
+SV_ASDU_addINT8(SVPublisher_ASDU self);
+
+LIB61850_API DEPRECATED void
+SV_ASDU_setINT8(SVPublisher_ASDU self, int index, int8_t value);
+
+LIB61850_API DEPRECATED int
+SV_ASDU_addINT32(SVPublisher_ASDU self);
+
+LIB61850_API DEPRECATED void
+SV_ASDU_setINT32(SVPublisher_ASDU self, int index, int32_t value);
+
+LIB61850_API DEPRECATED int
+SV_ASDU_addINT64(SVPublisher_ASDU self);
+
+LIB61850_API DEPRECATED void
+SV_ASDU_setINT64(SVPublisher_ASDU self, int index, int64_t value);
+
+LIB61850_API DEPRECATED int
+SV_ASDU_addFLOAT(SVPublisher_ASDU self);
+
+LIB61850_API DEPRECATED void
+SV_ASDU_setFLOAT(SVPublisher_ASDU self, int index, float value);
+
+LIB61850_API DEPRECATED int
+SV_ASDU_addFLOAT64(SVPublisher_ASDU self);
+
+LIB61850_API DEPRECATED void
+SV_ASDU_setFLOAT64(SVPublisher_ASDU self, int index, double value);
+
+LIB61850_API DEPRECATED void
+SV_ASDU_setSmpCnt(SVPublisher_ASDU self, uint16_t value);
+
+LIB61850_API DEPRECATED uint16_t
+SV_ASDU_getSmpCnt(SVPublisher_ASDU self);
+
+LIB61850_API DEPRECATED void
+SV_ASDU_increaseSmpCnt(SVPublisher_ASDU self);
+
+LIB61850_API DEPRECATED void
+SV_ASDU_setRefrTm(SVPublisher_ASDU self, uint64_t refrTm);
+
+LIB61850_API DEPRECATED void
+SV_ASDU_setSmpMod(SVPublisher_ASDU self, uint8_t smpMod);
+
+LIB61850_API DEPRECATED void
+SV_ASDU_setSmpRate(SVPublisher_ASDU self, uint16_t smpRate);
+
+/**@}*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBIEC61850_SRC_SAMPLED_VALUES_SV_PUBLISHER_H_ */

+ 569 - 0
library/include/libiec61850/sv_subscriber.h

@@ -0,0 +1,569 @@
+/*
+ *  sv_subscriber.h
+ *
+ *  Copyright 2015-2022 Michael Zillgith
+ *
+ *  This file is part of libIEC61850.
+ *
+ *  libIEC61850 is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  libIEC61850 is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with libIEC61850.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  See COPYING file for the complete license text.
+ */
+
+#ifndef SAMPLED_VALUES_SV_SUBSCRIBER_H_
+#define SAMPLED_VALUES_SV_SUBSCRIBER_H_
+
+#include "libiec61850_common_api.h"
+#include "iec61850_common.h"
+#include "r_session.h"
+#include "hal_ethernet.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \defgroup sv_subscriber_api_group IEC 61850 Sampled Values (SV) subscriber API
+ *
+ * The sampled values (SV) subscriber API consists of three different objects.
+ * The \ref SVReceiver object is responsible for handling all SV Ethernet messages
+ * for a specific Ethernet interface. If you want to receive SV messages on multiple
+ * Ethernet interfaces you have to use several \ref SVReceiver instances.
+ * An \ref SVSubscriber object is associated to a SV data stream that is identified by its appID
+ * and destination Ethernet address. The \reg SVSubscriber object is used to install a callback
+ * handler \ref SVUpdateListener that is invoked for each ASDU (application service data unit) received for the
+ * associated stream. An \ref SVSubscriber_ASDU is an object that represents a single ASDU. Each ASDU contains
+ * some meta information that can be obtained by specific access functions like e.g.
+ * \ref SVSubscriber_ASDU_getSmpCnt to access the "SmpCnt" (sample count) attribute of the ASDU. The actual
+ * measurement data contained in the ASDU does not consist of structured ASN.1 data but stored as raw binary
+ * data. Without a priori knowledge of the dataset associated with the ASDU data stream it is not
+ * possible to interpret the received data correctly. Therefore you have to provide the data access functions
+ * with an index value to indicate the data type and the start of the data in the data block of the ASDU.
+ * E.g. reading a data set consisting of two FLOAT32 values you can use two subsequent calls of
+ * \ref SVSubscriber_ASDU_getFLOAT32 one with index = 0 and the second one with index = 4.
+ *
+ *  | IEC 61850 type | required bytes |
+ *  | -------------- | -------------- |
+ *  | BOOLEAN        | 1 byte         |
+ *  | INT8           | 1 byte         |
+ *  | INT16          | 2 byte         |
+ *  | INT32          | 4 byte         |
+ *  | INT64          | 8 byte         |
+ *  | INT8U          | 1 byte         |
+ *  | INT16U         | 2 byte         |
+ *  | INT24U         | 3 byte         |
+ *  | INT32U         | 4 byte         |
+ *  | INT64U         | 8 byte         |
+ *  | FLOAT32        | 4 byte         |
+ *  | FLOAT64        | 8 byte         |
+ *  | ENUMERATED     | 4 byte         |
+ *  | CODED ENUM     | 4 byte         |
+ *  | OCTET STRING   | 20 byte        |
+ *  | VISIBLE STRING | 35 byte        |
+ *  | TimeStamp      | 8 byte         |
+ *  | EntryTime      | 6 byte         |
+ *  | BITSTRING      | 4 byte         |
+ *  | Quality        | 4 byte         |
+ *
+ * The SV subscriber API can be used independent of the IEC 61850 client API. In order to access the SVCB via MMS you
+ * have to use the IEC 61850 client API. Please see \ref ClientSVControlBlock object in section \ref IEC61850_CLIENT_SV.
+ *
+ */
+/**@{*/
+
+
+/**
+ * \brief opaque handle to a SV ASDU (Application service data unit) instance.
+ *
+ * Sampled Values (SV) ASDUs (application service data units) are the basic units for
+ * sampled value data. Each ASDU represents a single sample consisting of multiple measurement
+ * values with a single dedicated timestamp.
+ *
+ * NOTE: SVSubscriber_ASDU are statically allocated and are only valid inside of the SVUpdateListener
+ * function when called by the library. If you need the data contained in the ASDU elsewhere
+ * you have to copy and store the data by yourself!
+ */
+typedef struct sSVSubscriber_ASDU* SVSubscriber_ASDU;
+
+/**
+ * \brief opaque handle to a SV subscriber instance
+ *
+ * A subscriber is an instance associated with a single stream of measurement data. It is identified
+ * by the Ethernet destination address, the appID value (both are on SV message level) and the svID value
+ * that is part of each ASDU (SVSubscriber_ASDU object).
+ */
+typedef struct sSVSubscriber* SVSubscriber;
+
+/**
+ * \brief Callback function for received SV messages
+ *
+ * Will be called for each ASDU contained in a SV message!
+ *
+ * \param subscriber the subscriber that was associated with the received SV message
+ * \param parameter a user provided parameter that is simply passed to the callback
+ * \param asdu SV ASDU data structure. This structure is only valid inside of the  callback function
+ */
+typedef void (*SVUpdateListener)(SVSubscriber subscriber, void* parameter, SVSubscriber_ASDU asdu);
+
+/**
+ * \brief opaque handle to a SV receiver instance
+ */
+typedef struct sSVReceiver* SVReceiver;
+
+/**
+ * \brief Create a new SV receiver instance.
+ *
+ * A receiver is responsible for processing all SV message for a single Ethernet interface.
+ * In order to process messages from multiple Ethernet interfaces you have to create multiple
+ * instances.
+ *
+ * \return the newly created receiver instance
+ */
+LIB61850_API SVReceiver
+SVReceiver_create(void);
+
+/**
+ * \brief Create a new R-SV receiver instance.
+ *
+ * \param session the remote session protocol instance
+ *
+ * \return the newly created receiver instance
+ */
+LIB61850_API SVReceiver
+SVReceiver_createRemote(RSession session);
+
+/**
+ * \brief Disable check for destination address of the received SV messages
+ *
+ * \param self the receiver instance reference
+ */
+LIB61850_API void
+SVReceiver_disableDestAddrCheck(SVReceiver self);
+
+/**
+ * \brief Enable check for destination address of the received SV messages
+ *
+ * Per default only the appID is checked to identify relevant SV messages and the
+ * destination address is ignored for performance reasons. This only works when the
+ * appIDs are unique in the local system. Otherwise the destination address check
+ * has to be enabled.
+ *
+ * \param self the receiver instance reference
+ */
+LIB61850_API void
+SVReceiver_enableDestAddrCheck(SVReceiver self);
+
+/**
+ * \brief Set the Ethernet interface ID for the receiver instance
+ *
+ * Use this function if you want to use a different interface than
+ * the default interface set by CONFIG_ETHERNET_INTERFACE_ID (stack_config.h)
+ * NOTE: This function has to be called before calling SVReceiver_start.
+ *
+ * \param self the receiver instance reference
+ * \param interfaceId the Ethernet interface id (platform specific e.g. eth0 for linux).
+ */
+LIB61850_API void
+SVReceiver_setInterfaceId(SVReceiver self, const char* interfaceId);
+
+/**
+ * \brief Add a subscriber instance to the receiver
+ *
+ * The given subscriber will be connected to the receiver instance.
+ *
+ * \param self the receiver instance reference
+ * \param subscriber the subscriber instance to connect
+ */
+LIB61850_API void
+SVReceiver_addSubscriber(SVReceiver self, SVSubscriber subscriber);
+
+/**
+ * \brief Disconnect subscriber and receiver
+ *
+ * \param self the receiver instance reference
+ * \param subscriber the subscriber instance to disconnect
+ */
+LIB61850_API void
+SVReceiver_removeSubscriber(SVReceiver self, SVSubscriber subscriber);
+
+/**
+ * \brief Receiver starts listening for SV messages.
+ *
+ * NOTE: This call will start a new background thread.
+ *
+ * \param self the receiver instance reference
+ */
+LIB61850_API void
+SVReceiver_start(SVReceiver self);
+
+/**
+ * \brief Receiver stops listening for SV messages
+ *
+ * \param self the receiver instance reference
+ */
+LIB61850_API void
+SVReceiver_stop(SVReceiver self);
+
+/**
+ * \brief Check if SV receiver is running
+ *
+ * Can be used to check if \ref SVReceiver_start has been successful.
+ *
+ * \param self the receiver instance reference
+ *
+ * \return true if SV receiver is running, false otherwise
+ */
+LIB61850_API bool
+SVReceiver_isRunning(SVReceiver self);
+
+/**
+ * \brief Destroy receiver instance (cleanup resources)
+ *
+ * \param self the receiver instance reference
+ */
+LIB61850_API void
+SVReceiver_destroy(SVReceiver self);
+
+/***************************************
+ * Functions for non-threaded operation
+ ***************************************/
+
+LIB61850_API bool
+SVReceiver_startThreadless(SVReceiver self);
+
+LIB61850_API void
+SVReceiver_stopThreadless(SVReceiver self);
+
+/**
+ * \brief Parse SV messages if they are available.
+ *
+ * Call after reception of ethernet frame and periodically to to house keeping tasks
+ *
+ * \param self the receiver object
+ *
+ * \return true if a message was available and has been parsed, false otherwise
+ */
+LIB61850_API bool
+SVReceiver_tick(SVReceiver self);
+
+/*
+ * \brief Create a new SV subscriber instance
+ *
+ * \param ethAddr optional destination address (NULL to not specify the destination address)
+ * \param appID the APP-ID to identify matching SV messages
+ *
+ * \return the new subscriber instance
+ */
+
+LIB61850_API SVSubscriber
+SVSubscriber_create(const uint8_t* ethAddr, uint16_t appID);
+
+/**
+ * \brief Set a callback handler to process received SV messages
+ *
+ * If the received SV message contains multiple ASDUs (application service data units) the callback
+ * function will be called for each ASDU separately. If a callback function has already been installed
+ * for this SVSubscriber object the old callback will be replaced.
+ *
+ * \param self The subscriber object
+ * \param listener the callback function to install
+ * \param a user provided parameter that is provided to the callback function
+ */
+LIB61850_API void
+SVSubscriber_setListener(SVSubscriber self,  SVUpdateListener listener, void* parameter);
+
+LIB61850_API void
+SVSubscriber_destroy(SVSubscriber self);
+
+/*************************************************************************
+ * SVSubscriber_ASDU object methods
+ **************************************************************************/
+
+/**
+ * \addtogroup sv_subscriber_asdu_group Values Application Service Data Unit (ASDU)
+ *  @{
+ */
+
+/**
+ * \brief return the SmpCnt value included in the SV ASDU
+ *
+ * The SmpCnt (sample counter) is increased for each ASDU to
+ * identify the sample.
+ *
+ * \param self ASDU object instance
+ */
+LIB61850_API uint16_t
+SVSubscriber_ASDU_getSmpCnt(SVSubscriber_ASDU self);
+
+/**
+ * \brief return the SvID value included in the SV ASDU
+ *
+ * \param self ASDU object instance
+ */
+LIB61850_API const char*
+SVSubscriber_ASDU_getSvId(SVSubscriber_ASDU self);
+
+/**
+ * \brief return the DatSet value included in the SV ASDU
+ *
+ * \param self ASDU object instance
+ */
+LIB61850_API const char*
+SVSubscriber_ASDU_getDatSet(SVSubscriber_ASDU self);
+
+/**
+ * \brief return the ConfRev value included in the SV ASDU
+ *
+ * \param self ASDU object instance
+ */
+LIB61850_API uint32_t
+SVSubscriber_ASDU_getConfRev(SVSubscriber_ASDU self);
+
+/**
+ * \brief return the SmpMod value included in the SV ASDU
+ *
+ * \param self ASDU object instance
+ */
+LIB61850_API uint8_t
+SVSubscriber_ASDU_getSmpMod(SVSubscriber_ASDU self);
+
+/**
+ * \brief return the SmpRate value included in the SV ASDU
+ *
+ * \param self ASDU object instance
+ */
+LIB61850_API uint16_t
+SVSubscriber_ASDU_getSmpRate(SVSubscriber_ASDU self);
+
+/**
+ * \brief Check if DatSet value is included in the SV ASDU
+ *
+ * \param self ASDU object instance
+ *
+ * \return true if DatSet value is present, false otherwise
+ */
+LIB61850_API bool
+SVSubscriber_ASDU_hasDatSet(SVSubscriber_ASDU self);
+
+/**
+ * \brief Check if RefrTm value is included in the SV ASDU
+ *
+ * \param self ASDU object instance
+ *
+ * \return true if RefrTm value is present, false otherwise
+ */
+LIB61850_API bool
+SVSubscriber_ASDU_hasRefrTm(SVSubscriber_ASDU self);
+
+/**
+ * \brief Check if SmpMod value is included in the SV ASDU
+ *
+ * \param self ASDU object instance
+ *
+ * \return true if SmpMod value is present, false otherwise
+ */
+LIB61850_API bool
+SVSubscriber_ASDU_hasSmpMod(SVSubscriber_ASDU self);
+
+/**
+ * \brief Check if SmpRate value is included in the SV ASDU
+ *
+ * \param self ASDU object instance
+ *
+ * \return true if SmpRate value is present, false otherwise
+ */
+LIB61850_API bool
+SVSubscriber_ASDU_hasSmpRate(SVSubscriber_ASDU self);
+
+/**
+ * \brief Get the RefrTim value included in SV ASDU as milliseconds timestamp
+ *
+ * \param self ASDU object instance
+ *
+ * \return the time value as ms timestamp or 0 if RefrTm is not present in the SV ASDU
+ */
+LIB61850_API uint64_t
+SVSubscriber_ASDU_getRefrTmAsMs(SVSubscriber_ASDU self);
+
+/**
+ * \brief Get the RefrTim value included in SV ASDU as nanoseconds timestamp
+ *
+ * \param self ASDU object instance
+ *
+ * \return the time value as nanoseconds timestamp or 0 if RefrTm is not present in the SV ASDU
+ */
+LIB61850_API nsSinceEpoch
+SVSubscriber_ASDU_getRefrTmAsNs(SVSubscriber_ASDU self);
+
+/**
+ * \brief Get an INT8 data value in the data part of the ASDU
+ *
+ * \param self ASDU object instance
+ * \param index the index (byte position of the start) of the data in the data part
+ *
+ * \return SV data
+ */
+LIB61850_API int8_t
+SVSubscriber_ASDU_getINT8(SVSubscriber_ASDU self, int index);
+
+/**
+ * \brief Get an INT16 data value in the data part of the ASDU
+ *
+ * \param self ASDU object instance
+ * \param index the index (byte position of the start) of the data in the data part
+ *
+ * \return SV data
+ */
+LIB61850_API int16_t
+SVSubscriber_ASDU_getINT16(SVSubscriber_ASDU self, int index);
+
+/**
+ * \brief Get an INT32 data value in the data part of the ASDU
+ *
+ * \param self ASDU object instance
+ * \param index the index (byte position of the start) of the data in the data part
+ *
+ * \return SV data
+ */
+LIB61850_API int32_t
+SVSubscriber_ASDU_getINT32(SVSubscriber_ASDU self, int index);
+
+/**
+ * \brief Get an INT64 data value in the data part of the ASDU
+ *
+ * \param self ASDU object instance
+ * \param index the index (byte position of the start) of the data in the data part
+ *
+ * \return SV data
+ */
+LIB61850_API int64_t
+SVSubscriber_ASDU_getINT64(SVSubscriber_ASDU self, int index);
+
+/**
+ * \brief Get an INT8U data value in the data part of the ASDU
+ *
+ * \param self ASDU object instance
+ * \param index the index (byte position of the start) of the data in the data part
+ *
+ * \return SV data
+ */
+LIB61850_API uint8_t
+SVSubscriber_ASDU_getINT8U(SVSubscriber_ASDU self, int index);
+
+/**
+ * \brief Get an INT16U data value in the data part of the ASDU
+ *
+ * \param self ASDU object instance
+ * \param index the index (byte position of the start) of the data in the data part
+ *
+ * \return SV data
+ */
+LIB61850_API uint16_t
+SVSubscriber_ASDU_getINT16U(SVSubscriber_ASDU self, int index);
+
+/**
+ * \brief Get an INT32U data value in the data part of the ASDU
+ *
+ * \param self ASDU object instance
+ * \param index the index (byte position of the start) of the data in the data part
+ *
+ * \return SV data
+ */
+LIB61850_API uint32_t
+SVSubscriber_ASDU_getINT32U(SVSubscriber_ASDU self, int index);
+
+/**
+ * \brief Get an INT64U data value in the data part of the ASDU
+ *
+ * \param self ASDU object instance
+ * \param index the index (byte position of the start) of the data in the data part
+ *
+ * \return SV data
+ */
+LIB61850_API uint64_t
+SVSubscriber_ASDU_getINT64U(SVSubscriber_ASDU self, int index);
+
+/**
+ * \brief Get an FLOAT32 data value in the data part of the ASDU
+ *
+ * \param self ASDU object instance
+ * \param index the index (byte position of the start) of the data in the data part
+ *
+ * \return SV data
+ */
+LIB61850_API float
+SVSubscriber_ASDU_getFLOAT32(SVSubscriber_ASDU self, int index);
+
+/**
+ * \brief Get an FLOAT64 data value in the data part of the ASDU
+ *
+ * \param self ASDU object instance
+ * \param index the index (byte position of the start) of the data in the data part
+ *
+ * \return SV data
+ */
+LIB61850_API double
+SVSubscriber_ASDU_getFLOAT64(SVSubscriber_ASDU self, int index);
+
+/**
+ * \brief Get a timestamp data value in the data part of the ASDU
+ *
+ * \param self ASDU object instance
+ * \param index the index (byte position of the start) of the data in the data part
+ *
+ * \return SV data
+ */
+LIB61850_API Timestamp
+SVSubscriber_ASDU_getTimestamp(SVSubscriber_ASDU self, int index);
+
+/**
+ * \brief Get a quality value in the data part of the ASDU
+ *
+ * NOTE: Quality is encoded as BITSTRING (4 byte)
+ *
+ * \param self ASDU object instance
+ * \param index the index (byte position of the start) of the data in the data part
+ *
+ * \return SV data
+ */
+LIB61850_API Quality
+SVSubscriber_ASDU_getQuality(SVSubscriber_ASDU self, int index);
+
+/**
+ * \brief Returns the size of the data part of the ASDU
+ *
+ * \param self ASDU object instance
+ *
+ * \return size of the ASDU data part in bytes.
+ */
+LIB61850_API int
+SVSubscriber_ASDU_getDataSize(SVSubscriber_ASDU self);
+
+/**
+ * \brief return the SmpSynch value included in the SV ASDU
+ *
+ * The SmpSynch gives information about the clock synchronization.
+ *
+ * \param self ASDU object instance
+ */
+uint8_t
+SVSubscriber_ASDU_getSmpSynch(SVSubscriber_ASDU self);
+
+/** @}*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SAMPLED_VALUES_SV_SUBSCRIBER_ */

+ 262 - 0
library/include/libiec61850/tls_ciphers.h

@@ -0,0 +1,262 @@
+#ifndef IANA_TLS_CIPHER_SUITES_H_
+#define IANA_TLS_CIPHER_SUITES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define TLS_NULL_WITH_NULL_NULL 0x0000
+#define TLS_RSA_WITH_NULL_MD5 0x0001
+#define TLS_RSA_WITH_NULL_SHA 0x0002
+#define TLS_RSA_EXPORT_WITH_RC4_40_MD5 0x0003
+#define TLS_RSA_WITH_RC4_128_MD5 0x0004
+#define TLS_RSA_WITH_RC4_128_SHA 0x0005
+#define TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 0x0006
+#define TLS_RSA_WITH_IDEA_CBC_SHA 0x0007
+#define TLS_RSA_EXPORT_WITH_DES40_CBC_SHA 0x0008
+#define TLS_RSA_WITH_DES_CBC_SHA 0x0009
+#define TLS_RSA_WITH_3DES_EDE_CBC_SHA 0x000A
+#define TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA 0x000B
+#define TLS_DH_DSS_WITH_DES_CBC_SHA 0x000C
+#define TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA 0x000D
+#define TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA 0x000E
+#define TLS_DH_RSA_WITH_DES_CBC_SHA 0x000F
+#define TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA 0x0010
+#define TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA 0x0011
+#define TLS_DHE_DSS_WITH_DES_CBC_SHA 0x0012
+#define TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA 0x0013
+#define TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA 0x0014
+#define TLS_DHE_RSA_WITH_DES_CBC_SHA 0x0015
+#define TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA 0x0016
+#define TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 0x0017
+#define TLS_DH_anon_WITH_RC4_128_MD5 0x0018
+#define TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA 0x0019
+#define TLS_DH_anon_WITH_DES_CBC_SHA 0x001A
+#define TLS_DH_anon_WITH_3DES_EDE_CBC_SHA 0x001B
+#define TLS_RSA_WITH_AES_128_CBC_SHA 0x002F
+#define TLS_DH_DSS_WITH_AES_128_CBC_SHA 0x0030
+#define TLS_DH_RSA_WITH_AES_128_CBC_SHA 0x0031
+#define TLS_DHE_DSS_WITH_AES_128_CBC_SHA 0x0032
+#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA 0x0033
+#define TLS_DH_anon_WITH_AES_128_CBC_SHA 0x0034
+#define TLS_RSA_WITH_AES_256_CBC_SHA 0x0035
+#define TLS_DH_DSS_WITH_AES_256_CBC_SHA 0x0036
+#define TLS_DH_RSA_WITH_AES_256_CBC_SHA 0x0037
+#define TLS_DHE_DSS_WITH_AES_256_CBC_SHA 0x0038
+#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA 0x0039
+#define TLS_DH_anon_WITH_AES_256_CBC_SHA 0x003A
+#define TLS_RSA_WITH_NULL_SHA256 0x003B
+#define TLS_RSA_WITH_AES_128_CBC_SHA256 0x003C
+#define TLS_RSA_WITH_AES_256_CBC_SHA256 0x003D
+#define TLS_DH_DSS_WITH_AES_128_CBC_SHA256 0x003E
+#define TLS_DH_RSA_WITH_AES_128_CBC_SHA256 0x003F
+#define TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 0x0040
+#define TLS_RSA_WITH_CAMELLIA_128_CBC_SHA 0x0041
+#define TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA 0x0042
+#define TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA 0x0043
+#define TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA 0x0044
+#define TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA 0x0045
+#define TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA 0x0046
+#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 0x0067
+#define TLS_DH_DSS_WITH_AES_256_CBC_SHA256 0x0068
+#define TLS_DH_RSA_WITH_AES_256_CBC_SHA256 0x0069
+#define TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 0x006A
+#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 0x006B
+#define TLS_DH_anon_WITH_AES_128_CBC_SHA256 0x006C
+#define TLS_DH_anon_WITH_AES_256_CBC_SHA256 0x006D
+#define TLS_RSA_WITH_CAMELLIA_256_CBC_SHA 0x0084
+#define TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA 0x0085
+#define TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA 0x0086
+#define TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA 0x0087
+#define TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA 0x0088
+#define TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA 0x0089
+#define TLS_RSA_WITH_SEED_CBC_SHA 0x0096
+#define TLS_DH_DSS_WITH_SEED_CBC_SHA 0x0097
+#define TLS_DH_RSA_WITH_SEED_CBC_SHA 0x0098
+#define TLS_DHE_DSS_WITH_SEED_CBC_SHA 0x0099
+#define TLS_DHE_RSA_WITH_SEED_CBC_SHA 0x009A
+#define TLS_DH_anon_WITH_SEED_CBC_SHA 0x009B
+#define TLS_RSA_WITH_AES_128_GCM_SHA256 0x009C
+#define TLS_RSA_WITH_AES_256_GCM_SHA384 0x009D
+#define TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 0x009E
+#define TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 0x009F
+#define TLS_DH_RSA_WITH_AES_128_GCM_SHA256 0x00A0
+#define TLS_DH_RSA_WITH_AES_256_GCM_SHA384 0x00A1
+#define TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 0x00A2
+#define TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 0x00A3
+#define TLS_DH_DSS_WITH_AES_128_GCM_SHA256 0x00A4
+#define TLS_DH_DSS_WITH_AES_256_GCM_SHA384 0x00A5
+#define TLS_DH_anon_WITH_AES_128_GCM_SHA256 0x00A6
+#define TLS_DH_anon_WITH_AES_256_GCM_SHA384 0x00A7
+#define TLS_PSK_WITH_AES_128_CBC_SHA 0x008C
+#define TLS_PSK_WITH_AES_256_CBC_SHA 0x008D
+#define TLS_DHE_PSK_WITH_AES_128_CBC_SHA 0x008E
+#define TLS_DHE_PSK_WITH_AES_256_CBC_SHA 0x008F
+#define TLS_RSA_PSK_WITH_AES_128_CBC_SHA 0x0090
+#define TLS_RSA_PSK_WITH_AES_256_CBC_SHA 0x0091
+#define TLS_PSK_WITH_AES_128_CBC_SHA256 0x00AE
+#define TLS_PSK_WITH_AES_256_CBC_SHA384 0x00AF
+#define TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 0x00B0
+#define TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 0x00B1
+#define TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 0x00B2
+#define TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 0x00B3
+#define TLS_PSK_WITH_NULL_SHA 0x002C
+#define TLS_DHE_PSK_WITH_NULL_SHA 0x002D
+#define TLS_RSA_PSK_WITH_NULL_SHA 0x002E
+#define TLS_PSK_WITH_NULL_SHA256 0x00B4
+#define TLS_PSK_WITH_NULL_SHA384 0x00B5
+#define TLS_DHE_PSK_WITH_NULL_SHA256 0x00B6
+#define TLS_DHE_PSK_WITH_NULL_SHA384 0x00B7
+#define TLS_RSA_PSK_WITH_NULL_SHA256 0x00B8
+#define TLS_RSA_PSK_WITH_NULL_SHA384 0x00B9
+#define TLS_ECDH_ECDSA_WITH_NULL_SHA 0xC001
+#define TLS_ECDH_ECDSA_WITH_RC4_128_SHA 0xC002
+#define TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA 0xC003
+#define TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA 0xC004
+#define TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA 0xC005
+#define TLS_ECDHE_ECDSA_WITH_NULL_SHA 0xC006
+#define TLS_ECDHE_ECDSA_WITH_RC4_128_SHA 0xC007
+#define TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA 0xC008
+#define TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA 0xC009
+#define TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA 0xC00A
+#define TLS_ECDH_RSA_WITH_NULL_SHA 0xC00B
+#define TLS_ECDH_RSA_WITH_RC4_128_SHA 0xC00C
+#define TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA 0xC00D
+#define TLS_ECDH_RSA_WITH_AES_128_CBC_SHA 0xC00E
+#define TLS_ECDH_RSA_WITH_AES_256_CBC_SHA 0xC00F
+#define TLS_ECDHE_RSA_WITH_NULL_SHA 0xC010
+#define TLS_ECDHE_RSA_WITH_RC4_128_SHA 0xC011
+#define TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA 0xC012
+#define TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA 0xC013
+#define TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA 0xC014
+#define TLS_ECDH_anon_WITH_NULL_SHA 0xC015
+#define TLS_ECDH_anon_WITH_RC4_128_SHA 0xC016
+#define TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA 0xC017
+#define TLS_ECDH_anon_WITH_AES_128_CBC_SHA 0xC018
+#define TLS_ECDH_anon_WITH_AES_256_CBC_SHA 0xC019
+#define TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 0xC023
+#define TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 0xC024
+#define TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 0xC025
+#define TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 0xC026
+#define TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 0xC027
+#define TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 0xC028
+#define TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 0xC029
+#define TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 0xC02A
+#define TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 0xC02B
+#define TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 0xC02C
+#define TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 0xC02D
+#define TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 0xC02E
+#define TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 0xC02F
+#define TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 0xC030
+#define TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 0xC031
+#define TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 0xC032
+#define TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA 0xC035
+#define TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA 0xC036
+#define TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 0xC037
+#define TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 0xC038
+#define TLS_ECDHE_PSK_WITH_NULL_SHA 0xC039
+#define TLS_ECDHE_PSK_WITH_NULL_SHA256 0xC03A
+#define TLS_ECDHE_PSK_WITH_NULL_SHA384 0xC03B
+#define TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 0xC072
+#define TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 0xC073
+#define TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 0xC074
+#define TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 0xC075
+#define TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 0xC076
+#define TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 0xC077
+#define TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 0xC078
+#define TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 0xC079
+#define TLS_RSA_WITH_ARIA_128_CBC_SHA256 0xC03C
+#define TLS_RSA_WITH_ARIA_256_CBC_SHA384 0xC03D
+#define TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256 0xC03E
+#define TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384 0xC03F
+#define TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256 0xC040
+#define TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384 0xC041
+#define TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256 0xC042
+#define TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384 0xC043
+#define TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 0xC044
+#define TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 0xC045
+#define TLS_DH_anon_WITH_ARIA_128_CBC_SHA256 0xC046
+#define TLS_DH_anon_WITH_ARIA_256_CBC_SHA384 0xC047
+#define TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 0xC048
+#define TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 0xC049
+#define TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256 0xC04A
+#define TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384 0xC04B
+#define TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 0xC04C
+#define TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 0xC04D
+#define TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256 0xC04E
+#define TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384 0xC04F
+#define TLS_RSA_WITH_ARIA_128_GCM_SHA256 0xC050
+#define TLS_RSA_WITH_ARIA_256_GCM_SHA384 0xC051
+#define TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 0xC052
+#define TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 0xC053
+#define TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256 0xC054
+#define TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384 0xC055
+#define TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256 0xC056
+#define TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384 0xC057
+#define TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256 0xC058
+#define TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384 0xC059
+#define TLS_DH_anon_WITH_ARIA_128_GCM_SHA256 0xC05A
+#define TLS_DH_anon_WITH_ARIA_256_GCM_SHA384 0xC05B
+#define TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 0xC05C
+#define TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 0xC05D
+#define TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256 0xC05E
+#define TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384 0xC05F
+#define TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 0xC060
+#define TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 0xC061
+#define TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256 0xC062
+#define TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384 0xC063
+#define TLS_PSK_WITH_ARIA_128_CBC_SHA256 0xC064
+#define TLS_PSK_WITH_ARIA_256_CBC_SHA384 0xC065
+#define TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 0xC066
+#define TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 0xC067
+#define TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 0xC068
+#define TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 0xC069
+#define TLS_PSK_WITH_ARIA_128_GCM_SHA256 0xC06A
+#define TLS_PSK_WITH_ARIA_256_GCM_SHA384 0xC06B
+#define TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256 0xC06C
+#define TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384 0xC06D
+#define TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 0xC06E
+#define TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 0xC06F
+#define TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 0xC070
+#define TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 0xC071
+#define TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 0xC076
+#define TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 0xC077
+#define TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 0xC078
+#define TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 0xC079
+#define TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 0xC07A
+#define TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 0xC07B
+#define TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 0xC07C
+#define TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 0xC07D
+#define TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 0xC07E
+#define TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 0xC07F
+#define TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 0xC080
+#define TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 0xC081
+#define TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 0xC082
+#define TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 0xC083
+#define TLS_ECDHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 0xC084
+#define TLS_ECDHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 0xC085
+#define TLS_RSA_WITH_AES_128_CCM 0xC09C
+#define TLS_RSA_WITH_AES_256_CCM 0xC09D
+#define TLS_DHE_RSA_WITH_AES_128_CCM 0xC09E
+#define TLS_DHE_RSA_WITH_AES_256_CCM 0xC09F
+#define TLS_RSA_WITH_AES_128_CCM_8 0xC0A0
+#define TLS_RSA_WITH_AES_256_CCM_8 0xC0A1
+#define TLS_DHE_RSA_WITH_AES_128_CCM_8 0xC0A2
+#define TLS_DHE_RSA_WITH_AES_256_CCM_8 0xC0A3
+#define TLS_PSK_WITH_AES_128_CCM 0xC0A4
+#define TLS_PSK_WITH_AES_256_CCM 0xC0A5
+#define TLS_DHE_PSK_WITH_AES_128_CCM 0xC0A6
+#define TLS_DHE_PSK_WITH_AES_256_CCM 0xC0A7
+#define TLS_PSK_WITH_AES_128_CCM_8 0xC0A8
+#define TLS_PSK_WITH_AES_256_CCM_8 0xC0A9
+#define TLS_PSK_DHE_WITH_AES_128_CCM_8 0xC0AA
+#define TLS_PSK_DHE_WITH_AES_256_CCM_8 0xC0AB
+#define TLS_ECDHE_ECDSA_WITH_AES_128_CCM 0xC0AC
+
+// ... add more cipher suite codes here ...
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* IANA_TLS_CIPHER_SUITES_H_ */

+ 341 - 0
library/include/libiec61850/tls_config.h

@@ -0,0 +1,341 @@
+/*
+ * tls_config.h
+ *
+ * TLS Configuration API for protocol stacks using TCP/IP
+ *
+ * Copyright 2017-2024 Michael Zillgith
+ *
+ * Abstraction layer for configuration of different TLS implementations
+ *
+ */
+
+#ifndef SRC_TLS_CONFIG_H_
+#define SRC_TLS_CONFIG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "hal_base.h"
+#include "tls_ciphers.h"
+
+/**
+ * \file tls_config.h
+ * \brief TLS API functions
+ */
+
+/*! \addtogroup hal Platform (Hardware/OS) abstraction layer
+   *
+   *  @{
+   */
+
+/**
+ * @defgroup TLS_CONFIG_API TLS configuration
+ *
+ * @{
+ */
+
+typedef struct sTLSConfiguration* TLSConfiguration;
+
+/**
+ * \brief Create a new \ref TLSConfiguration object to represent TLS configuration and certificates
+ *
+ * WARNING: Configuration cannot be changed after using for the first time.
+ *
+ * \return the new TLS configuration
+ */
+PAL_API TLSConfiguration
+TLSConfiguration_create(void);
+
+/* will be called by stack automatically when appropriate */
+PAL_API void
+TLSConfiguration_setClientMode(TLSConfiguration self);
+
+typedef enum {
+   TLS_VERSION_NOT_SELECTED = 0,
+   TLS_VERSION_SSL_3_0 = 3,
+   TLS_VERSION_TLS_1_0 = 4,
+   TLS_VERSION_TLS_1_1 = 5,
+   TLS_VERSION_TLS_1_2 = 6,
+   TLS_VERSION_TLS_1_3 = 7
+} TLSConfigVersion;
+
+/**
+ * \brief Convert TLS version number to string
+ * 
+ * \param version TLS version number
+ * 
+ * \return the TLS version as null terminated string 
+ */
+PAL_API const char*
+TLSConfigVersion_toString(TLSConfigVersion version);
+
+typedef enum {
+    TLS_SEC_EVT_INFO = 0,
+    TLS_SEC_EVT_WARNING = 1,
+    TLS_SEC_EVT_INCIDENT = 2
+} TLSEventLevel;
+
+#define TLS_EVENT_CODE_ALM_ALGO_NOT_SUPPORTED 1
+#define TLS_EVENT_CODE_ALM_UNSECURE_COMMUNICATION 2
+#define TLS_EVENT_CODE_ALM_CERT_UNAVAILABLE 3
+#define TLS_EVENT_CODE_ALM_BAD_CERT 4
+#define TLS_EVENT_CODE_ALM_CERT_SIZE_EXCEEDED 5
+#define TLS_EVENT_CODE_ALM_CERT_VALIDATION_FAILED 6
+#define TLS_EVENT_CODE_ALM_CERT_REQUIRED 7
+#define TLS_EVENT_CODE_ALM_HANDSHAKE_FAILED_UNKNOWN_REASON 8
+#define TLS_EVENT_CODE_WRN_INSECURE_TLS_VERSION 9
+#define TLS_EVENT_CODE_INF_SESSION_RENEGOTIATION 10
+#define TLS_EVENT_CODE_ALM_CERT_EXPIRED 11
+#define TLS_EVENT_CODE_ALM_CERT_REVOKED 12
+#define TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED 13
+#define TLS_EVENT_CODE_ALM_CERT_NOT_TRUSTED 14
+#define TLS_EVENT_CODE_ALM_NO_CIPHER 15
+#define TLS_EVENT_CODE_INF_SESSION_ESTABLISHED 16
+
+typedef struct sTLSConnection* TLSConnection;
+
+/**
+ * \brief Get the peer address of the TLS connection
+ * 
+ * \param self the TLS connection instance
+ * \param peerAddrBuf user provided buffer that can hold at least 60 characters, or NULL to allow the function to allocate the memory for the buffer
+ * 
+ * \returns peer address:port as null terminated string 
+ */
+PAL_API char*
+TLSConnection_getPeerAddress(TLSConnection self, char* peerAddrBuf);
+
+/**
+ * \brief Get the TLS certificate used by the peer
+ * 
+ * \param self the TLS connection instance
+ * \param certSize[OUT] the certificate size in bytes
+ * 
+ * \return address of the certificate buffer 
+ */
+PAL_API uint8_t*
+TLSConnection_getPeerCertificate(TLSConnection self, int* certSize);
+
+/**
+ * \brief Get the TLS version used by the connection
+ * 
+ * \param self the TLS connection instance
+ * 
+ * \return TLS version
+ */
+PAL_API TLSConfigVersion
+TLSConnection_getTLSVersion(TLSConnection self);
+
+typedef void (*TLSConfiguration_EventHandler)(void* parameter, TLSEventLevel eventLevel, int eventCode, const char* message, TLSConnection con);
+
+/**
+ * \brief Set the security event handler
+ * 
+ * \param handler the security event callback handler
+ * \param parameter user provided parameter to be passed to the callback handler
+ */
+PAL_API void
+TLSConfiguration_setEventHandler(TLSConfiguration self, TLSConfiguration_EventHandler handler, void* parameter);
+
+/**
+ * \brief enable or disable TLS session resumption (default: enabled)
+ * 
+ * NOTE: Depending on the used TLS version this is implemented by
+ * session IDs or by session tickets.
+ * 
+ * \param enable true to enable session resumption, false otherwise
+ */
+PAL_API void
+TLSConfiguration_enableSessionResumption(TLSConfiguration self, bool enable);
+
+/**
+ * \brief Set the maximum life time of a cached TLS session for session resumption in seconds
+ *
+ * \param intervalInSeconds the maximum lifetime of a cached TLS session
+ */
+PAL_API void
+TLSConfiguration_setSessionResumptionInterval(TLSConfiguration self, int intervalInSeconds);
+
+/**
+ * \brief Enables the validation of the certificate trust chain (enabled by default)
+ *
+ * \param value true to enable chain validation, false to disable
+ */
+PAL_API void
+TLSConfiguration_setChainValidation(TLSConfiguration self, bool value);
+
+/**
+ * \brief Set if only known certificates are accepted.
+ *
+ * If set to true only known certificates are accepted. Connections with unknown certificates
+ * are rejected even if they are signed by a trusted authority.
+ *
+ * \param value true to enable setting, false otherwise
+ */
+PAL_API void
+TLSConfiguration_setAllowOnlyKnownCertificates(TLSConfiguration self, bool value);
+
+/**
+ * \brief Set own certificate (identity) from a byte buffer
+ *
+ * \param certificate the certificate buffer
+ * \param certLen the lenght of the certificate
+ *
+ * \return true, when the certificate was set, false otherwise (e.g. unknown certificate format)
+ */
+PAL_API bool
+TLSConfiguration_setOwnCertificate(TLSConfiguration self, uint8_t* certificate, int certLen);
+
+/**
+ * \brief Set own certificate (identity) from a certificate file
+ *
+ * \param filename of the certificate file
+ *
+ * \return true, when the certificate was set, false otherwise (e.g. unknown certificate format)
+ */
+PAL_API bool
+TLSConfiguration_setOwnCertificateFromFile(TLSConfiguration self, const char* filename);
+
+/**
+ * \brief Set the own private key from a byte buffer
+ *
+ * \param key the private key to use
+ * \param keyLen the length of the key
+ * \param password the password of the key or null if the key is not password protected
+ *
+ * \return true, when the key was set, false otherwise (e.g. unknown key format)
+ */
+PAL_API bool
+TLSConfiguration_setOwnKey(TLSConfiguration self, uint8_t* key, int keyLen, const char* keyPassword);
+
+/**
+ * \brief Set the own private key from a key file
+ *
+ * \param filename filename/path of the key file
+ * \param password the password of the key or null if the key is not password protected
+ *
+ * \return true, when the key was set, false otherwise (e.g. unknown key format)
+ */
+PAL_API bool
+TLSConfiguration_setOwnKeyFromFile(TLSConfiguration self, const char* filename, const char* keyPassword);
+
+/**
+ * Add a certificate to the list of allowed peer certificates from a byte buffer
+ *
+ * \param certificate the certificate buffer
+ * \param certLen the length of the certificate buffer
+ * \return true, when the certificate was set, false otherwise (e.g. unknown certificate format)
+ */
+PAL_API bool
+TLSConfiguration_addAllowedCertificate(TLSConfiguration self, uint8_t* certificate, int certLen);
+
+/**
+ * \brief Add a certificate to the list of allowed peer certificates
+ *
+ * \param filename filename of the certificate file
+ * \return true, when the certificate was set, false otherwise (e.g. unknown certificate format)
+ */
+PAL_API bool
+TLSConfiguration_addAllowedCertificateFromFile(TLSConfiguration self, const char* filename);
+
+/**
+ * \brief Add a CA certificate used to validate peer certificates from a byte buffer
+ *
+ * \param certificate the certificate buffer
+ * \param certLen the length of the certificate buffer
+ * \return true, when the certificate was set, false otherwise (e.g. unknown certificate format)
+ */
+PAL_API bool
+TLSConfiguration_addCACertificate(TLSConfiguration self, uint8_t* certificate, int certLen);
+
+/**
+ * \brief Add a CA certificate used to validate peer certificates from a file
+ *
+ * \param filename filename of the certificate file
+ * \return true, when the certificate was set, false otherwise (e.g. unknown certificate format)
+ */
+PAL_API bool
+TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* filename);
+
+/**
+ * \brief Set the renegotiation timeout.
+ *
+ * After the timeout elapsed a TLS session renegotiation has to occur.
+ *
+ * \param timeInMs session renegotiation timeout in milliseconds
+ */
+PAL_API void
+TLSConfiguration_setRenegotiationTime(TLSConfiguration self, int timeInMs);
+
+/**
+ * \brief Set minimal allowed TLS version to use
+ */
+PAL_API void
+TLSConfiguration_setMinTlsVersion(TLSConfiguration self, TLSConfigVersion version);
+
+/**
+ * \brief Set maximal allowed TLS version to use
+ */
+PAL_API void
+TLSConfiguration_setMaxTlsVersion(TLSConfiguration self, TLSConfigVersion version);
+
+/**
+ * \brief Add a CRL (certificate revocation list) from buffer
+ *
+ * \param crl the buffer containing the CRL
+ * \param crlLen the length of the CRL buffer
+ * \return true, when the CRL was imported, false otherwise (e.g. unknown format)
+ */
+PAL_API bool
+TLSConfiguration_addCRL(TLSConfiguration self, uint8_t* crl, int crlLen);
+
+/**
+ * \brief Add a CRL (certificate revocation list) from a file
+ *
+ * \param filename filename of the CRL file
+ * \return true, when the CRL was imported, false otherwise (e.g. unknown format)
+ */
+PAL_API bool
+TLSConfiguration_addCRLFromFile(TLSConfiguration self, const char* filename);
+
+/**
+ * \brief Removes any CRL (certificate revocation list) currently in use
+ */
+PAL_API void
+TLSConfiguration_resetCRL(TLSConfiguration self);
+
+/**
+ * \brief Add an allowed ciphersuite to the list of allowed ciphersuites
+ *
+ * \param self the TLS configuration instance
+ * \param ciphersuite the ciphersuite to add (IANA cipher suite ID)
+ */
+PAL_API void
+TLSConfiguration_addCipherSuite(TLSConfiguration self, int ciphersuite);
+
+/**
+ * \brief Clear the list of allowed ciphersuites
+ *
+ * \param self the TLS configuration instance
+ */
+PAL_API void
+TLSConfiguration_clearCipherSuiteList(TLSConfiguration self);
+
+/**
+ * Release all resource allocated by the TLSConfiguration instance
+ *
+ * NOTE: Do not use the object after calling this function!
+ */
+PAL_API void
+TLSConfiguration_destroy(TLSConfiguration self);
+
+/** @} */
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SRC_TLS_CONFIG_H_ */

BIN
library/lib/libiec61850.so


BIN
library/lib/libiec61850.so.1.6.0


+ 1 - 0
modules/gateway-scheduler/CMakeLists.txt

@@ -32,6 +32,7 @@ link_libraries(
         pthread
         paho-mqttpp3
         paho-mqtt3as
+        iec61850
 )
 
 

+ 30 - 0
modules/gateway-scheduler/Makefile

@@ -233,6 +233,33 @@ src/main.cpp.s:
 	cd /home/ubuntu/embedded-gateway && $(MAKE) -f modules/gateway-scheduler/CMakeFiles/gateway-scheduler.dir/build.make modules/gateway-scheduler/CMakeFiles/gateway-scheduler.dir/src/main.cpp.s
 .PHONY : src/main.cpp.s
 
+src/report/MyIec61850Server.o: src/report/MyIec61850Server.cpp.o
+
+.PHONY : src/report/MyIec61850Server.o
+
+# target to build an object file
+src/report/MyIec61850Server.cpp.o:
+	cd /home/ubuntu/embedded-gateway && $(MAKE) -f modules/gateway-scheduler/CMakeFiles/gateway-scheduler.dir/build.make modules/gateway-scheduler/CMakeFiles/gateway-scheduler.dir/src/report/MyIec61850Server.cpp.o
+.PHONY : src/report/MyIec61850Server.cpp.o
+
+src/report/MyIec61850Server.i: src/report/MyIec61850Server.cpp.i
+
+.PHONY : src/report/MyIec61850Server.i
+
+# target to preprocess a source file
+src/report/MyIec61850Server.cpp.i:
+	cd /home/ubuntu/embedded-gateway && $(MAKE) -f modules/gateway-scheduler/CMakeFiles/gateway-scheduler.dir/build.make modules/gateway-scheduler/CMakeFiles/gateway-scheduler.dir/src/report/MyIec61850Server.cpp.i
+.PHONY : src/report/MyIec61850Server.cpp.i
+
+src/report/MyIec61850Server.s: src/report/MyIec61850Server.cpp.s
+
+.PHONY : src/report/MyIec61850Server.s
+
+# target to generate assembly for a file
+src/report/MyIec61850Server.cpp.s:
+	cd /home/ubuntu/embedded-gateway && $(MAKE) -f modules/gateway-scheduler/CMakeFiles/gateway-scheduler.dir/build.make modules/gateway-scheduler/CMakeFiles/gateway-scheduler.dir/src/report/MyIec61850Server.cpp.s
+.PHONY : src/report/MyIec61850Server.cpp.s
+
 src/report/TaskReportService.o: src/report/TaskReportService.cpp.o
 
 .PHONY : src/report/TaskReportService.o
@@ -902,6 +929,9 @@ help:
 	@echo "... src/main.o"
 	@echo "... src/main.i"
 	@echo "... src/main.s"
+	@echo "... src/report/MyIec61850Server.o"
+	@echo "... src/report/MyIec61850Server.i"
+	@echo "... src/report/MyIec61850Server.s"
 	@echo "... src/report/TaskReportService.o"
 	@echo "... src/report/TaskReportService.i"
 	@echo "... src/report/TaskReportService.s"

+ 18 - 3
modules/gateway-scheduler/src/main.cpp

@@ -6,7 +6,7 @@
 #include "gateway-scheduler/src/service/ScheduleService.h"
 #include "gateway-scheduler/src/report/TaskReportService.h"
 #include "gateway-scheduler/src/tcp_server/TcpServer.h"
-
+#include "gateway-scheduler/src/report/MyIec61850Server.h"
 
 void terminate() { std::exit(1); }
 
@@ -16,22 +16,37 @@ int main(int argc, char* argv[]) {
   std::set_terminate(terminate);
   MESSAGE_PRINT(" scheduler starting");
   try {
+    /*
+    int tcpPort = 102;
+
+    if (argc > 1) {
+        tcpPort = atoi(argv[1]);
+    }
+
+    MyIec61850::MyIec61850Server server(tcpPort);
+
+    server.start();
+    server.run();
+    server.stop();
+    */
+    /*
     int port = 512;
     if(argc == 2){
       port = std::stoi(argv[1]);
     }
     TcpServer server(port);
     server.start_server();
+    */
 /*
     auto report = new TaskReportService();
     report->start();
-  
+*/ 
 
     auto queue = new QueueService();
     queue->initializeQueue();
     auto scheduler = new ScheduleService();
     scheduler->schedule();
-*/
+
 
   } catch (std::exception& exception) {
     MESSAGE_PRINT(" enter the catch function ");

+ 103 - 0
modules/gateway-scheduler/src/report/MyIec61850Server.cpp

@@ -0,0 +1,103 @@
+#include "MyIec61850Server.h"
+
+static int running = 0;
+
+void sigint_handler(int signalId) {
+    running = 0;
+}
+
+using namespace MyIec61850;
+
+MyIec61850Server::MyIec61850Server(int port) : port(port), iedServer(nullptr), model(nullptr),
+                                               lDevice1(nullptr), lln0(nullptr), ttmp1(nullptr),
+                                               temperatureValue(nullptr), temperatureTimestamp(nullptr),
+                                               hhum1(nullptr), humidityValue(nullptr), humidityTimestamp(nullptr),
+                                               dataSet(nullptr) {
+    setupDataModel();
+}
+
+MyIec61850Server::~MyIec61850Server() {
+    stop();
+    if (model) {
+        IedModel_destroy(model);
+    }
+}
+
+void MyIec61850Server::setupDataModel() {
+    model = IedModel_create("TemperatureHumiditySensor");
+
+    lDevice1 = LogicalDevice_create("SENSORS", model);
+
+    lln0 = LogicalNode_create("LLN0", lDevice1);
+
+    /* Create a Temperature Sensor LN */
+    ttmp1 = LogicalNode_create("TTMP1", lDevice1);
+    DataObject* ttmp1_tmpsv = CDC_SAV_create("TmpSv", (ModelNode*) ttmp1, 0, false);
+
+    temperatureValue = (DataAttribute*) ModelNode_getChild((ModelNode*) ttmp1_tmpsv, "instMag.f");
+    temperatureTimestamp = (DataAttribute*) ModelNode_getChild((ModelNode*) ttmp1_tmpsv, "t");
+
+    /* Create a Humidity Sensor LN */
+    hhum1 = LogicalNode_create("HHUM1", lDevice1);
+    DataObject* hhum1_humsv = CDC_SAV_create("HumSv", (ModelNode*) hhum1, 0, false);
+
+    humidityValue = (DataAttribute*) ModelNode_getChild((ModelNode*) hhum1_humsv, "instMag.f");
+    humidityTimestamp = (DataAttribute*) ModelNode_getChild((ModelNode*) hhum1_humsv, "t");
+
+    /* Create DataSet for reporting */
+    dataSet = DataSet_create("sensorData", lln0);
+    DataSetEntry_create(dataSet, "TTMP1$MX$TmpSv$instMag$f", -1, NULL);
+    DataSetEntry_create(dataSet, "HHUM1$MX$HumSv$instMag$f", -1, NULL);
+
+    /* Create Report Control Block */
+    uint8_t rptOptions = RPT_OPT_SEQ_NUM | RPT_OPT_TIME_STAMP | RPT_OPT_REASON_FOR_INCLUSION;
+    ReportControlBlock_create("sensorReport", lln0, "sensorReport", false, NULL, 1, TRG_OPT_DATA_CHANGED, rptOptions, 50, 0);
+}
+
+void MyIec61850Server::start() {
+    iedServer = IedServer_create(model); // 确保使用 sIedServer*
+
+    /* MMS server will be instructed to start listening to client connections. */
+    IedServer_start(iedServer, port);
+
+    if (!IedServer_isRunning(iedServer)) { // 使用 IedServer_isRunning 检查服务器状态
+        printf("Starting server failed! Exit.\n");
+        stop();
+        exit(-1);
+    }
+}
+
+void MyIec61850Server::stop() {
+    if (iedServer != nullptr) {
+        IedServer_stop(iedServer);
+        IedServer_destroy(iedServer); // 传递 sIedServer* 类型的参数
+        iedServer = nullptr; // 手动将指针置为 nullptr
+    }
+}
+
+void MyIec61850Server::run() {
+    running = 1;
+    signal(SIGINT, sigint_handler);
+
+    float temperature = 25.0f; // Initial temperature value
+    float humidity = 50.0f;    // Initial humidity value
+
+    while (running) {
+        IedServer_lockDataModel(iedServer);
+
+        /* Update temperature and humidity values */
+        IedServer_updateUTCTimeAttributeValue(iedServer, temperatureTimestamp, Hal_getTimeInMs());
+        IedServer_updateFloatAttributeValue(iedServer, temperatureValue, temperature);
+
+        IedServer_updateUTCTimeAttributeValue(iedServer, humidityTimestamp, Hal_getTimeInMs());
+        IedServer_updateFloatAttributeValue(iedServer, humidityValue, humidity);
+
+        IedServer_unlockDataModel(iedServer);
+
+        /* Simulate periodic updates */
+        temperature += 0.1f;
+        humidity += 0.1f;
+
+        Thread_sleep(1000); // Sleep for 1 second
+    }
+}

+ 42 - 0
modules/gateway-scheduler/src/report/MyIec61850Server.h

@@ -0,0 +1,42 @@
+#ifndef MY_IEC61850_SERVER_H
+#define MY_IEC61850_SERVER_H
+
+#include <libiec61850/iec61850_server.h>
+#include <libiec61850/hal_thread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+namespace MyIec61850 {
+
+class MyIec61850Server {
+public:
+    MyIec61850Server(int port = 102);
+    ~MyIec61850Server();
+
+    void start();
+    void stop();
+    void run();
+
+private:
+    void setupDataModel(); // 声明 setupDataModel 方法
+
+    int port;
+    sIedServer* iedServer; // 确保使用 sIedServer* 类型
+    IedModel* model;
+
+    LogicalDevice* lDevice1;
+    LogicalNode* lln0;
+    LogicalNode* ttmp1;
+    DataAttribute* temperatureValue;
+    DataAttribute* temperatureTimestamp;
+    LogicalNode* hhum1;
+    DataAttribute* humidityValue;
+    DataAttribute* humidityTimestamp;
+
+    DataSet* dataSet;
+};
+
+} // namespace MyIec61850
+
+#endif // MY_IEC61850_SERVER_H

+ 25 - 25
modules/gateway-scheduler/src/service/QueueService.cpp

@@ -38,7 +38,7 @@ void QueueService::initializeQueue() { /*?*/
     taskIds.push_back(task.id);
   }
   TaskFormatter formatterFilter;
-  formatterFilter.taskId = 0; //手动初始化
+  formatterFilter.taskId = 0; //锟街讹拷锟斤拷始锟斤拷
   formatterFilter.taskIds = taskIds;
   auto formatters = formatterService->list(formatterFilter);
   if (formatters.size() == 0) return;
@@ -63,32 +63,32 @@ void QueueService::initializeQueue() { /*?*/
     std::string task_name;
     std::string formula_str;
     std::string name ;
-    //获取设备名称 1,查询gateway_server_task,获取deviceId和name  2,查询gateway_server_device,获取name
+    //锟斤拷取锟借备锟斤拷锟斤拷 1锟斤拷锟斤拷询gateway_server_task锟斤拷锟斤拷取deviceId锟斤拷name  2锟斤拷锟斤拷询gateway_server_device锟斤拷锟斤拷取name
     sqlite3 *db;
     char *zErrMsg = 0;
     int rc;
-    // 打开数据库
-    rc = sqlite3_open("/home/lubancat/embedded-gateway/database/sqlite/gateway_server_task.db", &db);
+    // 锟斤拷锟斤拷锟捷匡拷
+    rc = sqlite3_open("/var/local/bin/database/sqlite/gateway_server_task.db", &db);
     if (rc) {
         std::cerr << "Can't open database: " << sqlite3_errmsg(db) << std::endl;
         return;
     }
 
-    // 准备 SQL 查
+    // 准锟斤拷 SQL 锟斤拷
     char *sql = "SELECT deviceId, name FROM gateway_server_task WHERE id = ?";
     sqlite3_stmt *stmt;
 
-    // 准备 SQL 语句
+    // 准锟斤拷 SQL 锟斤拷锟�
     rc = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr);
     if (rc != SQLITE_OK) {
         std::cerr << "SQL prepare error: " << sqlite3_errmsg(db) << std::endl;
         sqlite3_close(db);
         return;
     }
-    // 绑定参数
+    // 锟襟定诧拷锟斤拷
     sqlite3_bind_int(stmt, 1, formatter.taskId);
 
-    // 执行查询并处理结果
+    // 执锟叫诧拷询锟斤拷锟斤拷锟斤拷锟斤拷锟�
     if (sqlite3_step(stmt) == SQLITE_ROW) {
         const unsigned char *deviceId = sqlite3_column_text(stmt, 0);
         const unsigned char *devicename1 = sqlite3_column_text(stmt, 1);
@@ -104,32 +104,32 @@ void QueueService::initializeQueue() { /*?*/
     } else {
         std::cout << "No record found for id = " << task_id << std::endl;
     }
-    // 清理
+    // 锟斤拷锟斤拷
     sqlite3_finalize(stmt);
     sqlite3_close(db);
 
-    // 打开数据库
-    rc = sqlite3_open("/home/lubancat/embedded-gateway/database/sqlite/gateway_server_device.db", &db);
+    // 锟斤拷锟斤拷锟捷匡拷
+    rc = sqlite3_open("/var/local/bin/database/sqlite/gateway_server_device.db", &db);
     if (rc) {
         std::cerr << "Can't open database: " << sqlite3_errmsg(db) << std::endl;
         return;
     }
 
-    // 准备 SQL 查
+    // 准锟斤拷 SQL 锟斤拷
     sql = "SELECT name FROM gateway_server_device WHERE id = ?";
     //sqlite3_stmt *stmt;
 
-    // 准备 SQL 语句
+    // 准锟斤拷 SQL 锟斤拷锟�
     rc = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr);
     if (rc != SQLITE_OK) {
         std::cerr << "SQL prepare error: " << sqlite3_errmsg(db) << std::endl;
         sqlite3_close(db);
         return;
     }
-    // 绑定参数
+    // 锟襟定诧拷锟斤拷
     sqlite3_bind_int(stmt, 1, device_id);
 
-    // 执行查询并处理结果
+    // 执锟叫诧拷询锟斤拷锟斤拷锟斤拷锟斤拷锟�
     if (sqlite3_step(stmt) == SQLITE_ROW) {
         const unsigned char *devicename1 = sqlite3_column_text(stmt, 0);
         devicename.assign(reinterpret_cast<const char *>(devicename1));
@@ -141,33 +141,33 @@ void QueueService::initializeQueue() { /*?*/
     } else {
         std::cout << "No record found for id = " << task_id << std::endl;
     }
-    // 清理
+    // 锟斤拷锟斤拷
     sqlite3_finalize(stmt);
     sqlite3_close(db);
 
-    //获取值。暂时都配一个寄存器,取第二个字节,*0.1
-    //获取formatter :查询gateway_server_task_formatter表,根据id和taskId,获取format
-    // 打开数据库
-    rc = sqlite3_open("/home/lubancat/embedded-gateway/database/sqlite/gateway_server_task_formatter.db", &db);
+    //锟斤拷取值锟斤拷锟斤拷时锟斤拷锟斤拷一锟斤拷锟侥达拷锟斤拷锟斤拷取锟节讹拷锟斤拷锟街节o拷*0.1
+    //锟斤拷取formatter 锟斤拷锟斤拷询gateway_server_task_formatter锟斤拷锟斤拷锟斤拷锟斤拷id锟斤拷taskId锟斤拷锟斤拷取format
+    // 锟斤拷锟斤拷锟捷匡拷
+    rc = sqlite3_open("/var/local/bin/database/sqlite/gateway_server_task_formatter.db", &db);
     if (rc) {
         std::cerr << "Can't open database: " << sqlite3_errmsg(db) << std::endl;
         return;
     }
-    // 准备 SQL 查
+    // 准锟斤拷 SQL 锟斤拷
     sql = "SELECT format,formula,name FROM gateway_server_task_formatter WHERE id = ? and taskId = ?";
 
-    // 准备 SQL 语句
+    // 准锟斤拷 SQL 锟斤拷锟�
     rc = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr);
     if (rc != SQLITE_OK) {
         std::cerr << "SQL prepare error: " << sqlite3_errmsg(db) << std::endl;
         sqlite3_close(db);
         return;
     }
-    // 绑定参数
+    // 锟襟定诧拷锟斤拷
     sqlite3_bind_int(stmt, 1, formatter.id);
     sqlite3_bind_int(stmt, 2, formatter.taskId);
 
-    // 执行查询并处理结果
+    // 执锟叫诧拷询锟斤拷锟斤拷锟斤拷锟斤拷锟�
     if (sqlite3_step(stmt) == SQLITE_ROW) {
         const unsigned char *formatter1 = sqlite3_column_text(stmt, 0);
         formatter2.assign(reinterpret_cast<const char *>(formatter1));
@@ -184,7 +184,7 @@ void QueueService::initializeQueue() { /*?*/
     } else {
         std::cout << "No record found for formatter_id  task_id " << formatter.id << formatter.taskId << std::endl;
     }
-    // 清理
+    // 锟斤拷锟斤拷
     sqlite3_finalize(stmt);
     sqlite3_close(db);