- updated to version 0.8.3-dev

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

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

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

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

@ -1,121 +0,0 @@
/*
* byte_stream.c
*
* Some helper functions to read from sockets
*
* Copyright 2013 Michael Zillgith
*
* This file is part of libIEC61850.
*
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
#include "libiec61850_platform_includes.h"
#include "stack_config.h"
#include "byte_stream.h"
#include "socket.h"
int
ByteStream_readUint8(Socket self, uint8_t* byte)
{
int bytesRead;
uint64_t start = Hal_getTimeInMs();
do {
bytesRead = Socket_read(self, byte, 1);
if (bytesRead == -1) break;
} while ((bytesRead < 1) && ((Hal_getTimeInMs() - start) < CONFIG_TCP_READ_TIMEOUT_MS));
if (bytesRead != 1) {
return -1;
}
return 1;
}
int
ByteStream_readUint16(Socket self, uint16_t* value)
{
uint8_t byte[2];
int bytes_read = 0;
uint64_t start = Hal_getTimeInMs();
do {
int readVal = Socket_read(self, byte + bytes_read, 2);
if (readVal == -1) break;
bytes_read += readVal;
} while ((bytes_read < 2)
&& ((Hal_getTimeInMs() - start) < CONFIG_TCP_READ_TIMEOUT_MS));
if (bytes_read != 2)
return -1;
*value = (byte[0] * 0x100) + byte[1];
return 2;
}
int
ByteStream_skipBytes(Socket self, int number)
{
int c = 0;
uint8_t byte;
uint64_t start = Hal_getTimeInMs();
do {
int readBytes = Socket_read(self, &byte, 1);
if (readBytes < 0)
return -1;
else
c = c + readBytes;
} while ((c < number)
&& ((Hal_getTimeInMs() - start) < CONFIG_TCP_READ_TIMEOUT_MS));
return c;
}
int
ByteStream_readOctets(Socket self, uint8_t* buffer, int size)
{
int readBytes = 0;
int remainingSize = size;
uint64_t start = Hal_getTimeInMs();
do {
int chunkSize = Socket_read(self, buffer + readBytes, remainingSize);
if (chunkSize < 0)
return -1;
else
{
readBytes += chunkSize;
remainingSize = size - readBytes;
}
} while ((readBytes < size)
&& ((Hal_getTimeInMs() - start) < CONFIG_TCP_READ_TIMEOUT_MS));
return readBytes;
}

@ -1,43 +0,0 @@
/*
* byte_stream.h
*
* Copyright 2013 Michael Zillgith
*
* This file is part of libIEC61850.
*
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
#ifndef BYTE_STREAM_H_
#define BYTE_STREAM_H_
#include "libiec61850_platform_includes.h"
#include "socket.h"
#include "byte_buffer.h"
int
ByteStream_readUint8(Socket self, uint8_t* byte);
int
ByteStream_readUint16(Socket self, uint16_t* value);
int
ByteStream_skipBytes(Socket self, int number);
int
ByteStream_readOctets(Socket self, uint8_t* targetBuffer, int size);
#endif /* BYTE_STREAM_H_ */

@ -0,0 +1,65 @@
/*
* memory.h
*
* Copyright 2014 Michael Zillgith
*
* This file is part of libIEC61850.
*
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
#ifndef MEMORY_H_
#define MEMORY_H_
#define CALLOC(nmemb, size) Memory_calloc(nmemb, size)
#define MALLOC(size) Memory_malloc(size)
#define REALLOC(oldptr, size) Memory_realloc(oldptr, size)
#define FREEMEM(ptr) Memory_free(ptr)
#define GLOBAL_CALLOC(nmemb, size) Memory_calloc(nmemb, size)
#define GLOBAL_MALLOC(size) Memory_malloc(size)
#define GLOBAL_REALLOC(oldptr, size) Memory_realloc(oldptr, size)
#define GLOBAL_FREEMEM(ptr) Memory_free(ptr)
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
typedef void
(*MemoryExceptionHandler) (void* parameter);
void
Memory_installExceptionHandler(MemoryExceptionHandler handler, void* parameter);
void*
Memory_malloc(size_t size);
void*
Memory_calloc(size_t nmemb, size_t size);
void *
Memory_realloc(void *ptr, size_t size);
void
Memory_free(void* memb);
#ifdef __cplusplus
}
#endif
#endif /* MEMORY_H_ */

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

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

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

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

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

@ -0,0 +1,234 @@
/*
* socket_hal.h
*
* Copyright 2013, 2014 Michael Zillgith
*
* This file is part of libIEC61850.
*
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
#ifndef SOCKET_HAL_H_
#define SOCKET_HAL_H_
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup hal Platform (Hardware/OS) abstraction layer
*
* @{
*/
/**
* @defgroup HAL_SOCKET Interface to the TCP/IP stack (abstract socket layer)
*
* Thread and Socket abstraction layer. This functions have to be implemented to
* port libIEC61850 to a new hardware/OS platform.
*
* @{
*/
/** Opaque reference for a server socket instance */
typedef struct sServerSocket* ServerSocket;
/** Opaque reference for a client or connection socket instance */
typedef struct sSocket* Socket;
/** Opaque reference for a set of server and socket handles */
typedef struct sHandleSet* HandleSet;
HandleSet
Handleset_new(void);
void
Handleset_addSocket(HandleSet self, const Socket sock);
int
Handleset_waitReady(HandleSet self, unsigned int timeoutMs);
void
Handleset_destroy(HandleSet self);
/**
* \brief Create a new TcpServerSocket instance
*
* Implementation of this function is MANDATORY if server functionality is required.
*
* \param address ip address or hostname to listen on
* \param port the TCP port to listen on
*
* \return the newly create TcpServerSocket instance
*/
ServerSocket
TcpServerSocket_create(const char* address, int port);
void
ServerSocket_listen(ServerSocket self);
/**
* \brief accept a new incoming connection (non-blocking)
*
* This function shall accept a new incoming connection. It is non-blocking and has to
* return NULL if no new connection is pending.
*
* Implementation of this function is MANDATORY if server functionality is required.
*
* NOTE: The behaviour of this function changed with version 0.8!
*
* \param self server socket instance
*
* \return handle of the new connection socket or NULL if no new connection is available
*/
Socket
ServerSocket_accept(ServerSocket self);
/**
* \brief set the maximum number of pending connection in the queue
*
* Implementation of this function is OPTIONAL.
*
* \param self the server socket instance
* \param backlog the number of pending connections in the queue
*
*/
void
ServerSocket_setBacklog(ServerSocket self, int backlog);
/**
* \brief destroy a server socket instance
*
* Free all resources allocated by this server socket instance.
*
* Implementation of this function is MANDATORY if server functionality is required.
*
* \param self server socket instance
*/
void
ServerSocket_destroy(ServerSocket self);
/**
* \brief create a TCP client socket
*
* Implementation of this function is MANDATORY if client functionality is required.
*
* \return a new client socket instance.
*/
Socket
TcpSocket_create(void);
/**
* \brief set the timeout to establish a new connection
*
* \param self the client socket instance
* \param timeoutInMs the timeout in ms
*/
void
Socket_setConnectTimeout(Socket self, uint32_t timeoutInMs);
/**
* \brief connect to a server
*
* Connect to a server application identified by the address and port parameter.
*
* The "address" parameter may either be a hostname or an IP address. The IP address
* has to be provided as a C string (e.g. "10.0.2.1").
*
* Implementation of this function is MANDATORY if client functionality is required.
*
* NOTE: return type changed from int to bool with version 0.8
*
* \param self the client socket instance
* \param address the IP address or hostname as C string
* \param port the TCP port of the application to connect to
*
* \return a new client socket instance.
*/
bool
Socket_connect(Socket self, const char* address, int port);
/**
* \brief read from socket to local buffer (non-blocking)
*
* The function shall return immediately if no data is available. In this case
* the function returns 0. If an error happens the function shall return -1.
*
* Implementation of this function is MANDATORY
*
* NOTE: The behaviour of this function changed with version 0.8!
*
* \param self the client, connection or server socket instance
* \param buf the buffer where the read bytes are copied to
* \param size the maximum number of bytes to read (size of the provided buffer)
*
* \return the number of bytes read or -1 if an error occurred
*/
int
Socket_read(Socket self, uint8_t* buf, int size);
/**
* \brief send a message through the socket
*
* Implementation of this function is MANDATORY
*
* \param self client, connection or server socket instance
*
* \return number of bytes transmitted of -1 in case of an error
*/
int
Socket_write(Socket self, uint8_t* buf, int size);
/**
* \brief Get the address of the peer application (IP address and port number)
*
* The peer address has to be returned as
*
* Implementation of this function is MANDATORY
*
* \param self the client, connection or server socket instance
*
* \return the IP address and port number as strings separated by the ':' character.
*/
char*
Socket_getPeerAddress(Socket self);
/**
* \brief destroy a socket (close the socket if a connection is established)
*
* This function shall close the connection (if one is established) and free all
* resources allocated by the socket.
*
* Implementation of this function is MANDATORY
*
* \param self the client, connection or server socket instance
*/
void
Socket_destroy(Socket self);
/*! @} */
/*! @} */
#ifdef __cplusplus
}
#endif
#endif /* SOCKET_HAL_H_ */

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

@ -1,104 +0,0 @@
/*
* socket.h
*
* Copyright 2013 Michael Zillgith
*
* This file is part of libIEC61850.
*
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
#ifndef SOCKET_H_
#define SOCKET_H_
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup hal Hardware/OS abstraction layer
*
* @{
*/
/**
* @defgroup HAL_SOCKET Interface to the TCP/IP stack (abstract socket layer)
*
* Thread and Socket abstraction layer. This functions have to be implemented to
* port libIEC61850 to a new hardware/OS platform.
*
* @{
*/
/** Opaque reference for a server socket instance */
typedef struct sServerSocket* ServerSocket;
/** Opaque reference for a client or connection socket instance */
typedef struct sSocket* Socket;
/**
* Create a new TcpServerSocket instance
*
* \param address ip address or hostname to listen on
* \param port the TCP port to listen on
*
* \return the newly create TcpServerSocket instance
*/
ServerSocket
TcpServerSocket_create(char* address, int port);
void
ServerSocket_listen(ServerSocket self);
Socket
ServerSocket_accept(ServerSocket self);
void
ServerSocket_setBacklog(ServerSocket self, int backlog);
void
ServerSocket_destroy(ServerSocket self);
Socket
TcpSocket_create(void);
int
Socket_connect(Socket self, char* address, int port);
int
Socket_read(Socket self, uint8_t* buf, int size);
int
Socket_write(Socket self, uint8_t* buf, int size);
char*
Socket_getPeerAddress(Socket self);
void
Socket_destroy(Socket self);
/*! @} */
/*! @} */
#ifdef __cplusplus
}
#endif
#endif /* SOCKET_H_ */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -1,50 +1,56 @@
/*
* mms_mapping_internal.h
*
* Copyright 2013 Michael Zillgith
*
* This file is part of libIEC61850.
*
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
#ifndef MMS_MAPPING_INTERNAL_H_
#define MMS_MAPPING_INTERNAL_H_
#include "thread.h"
#include "linked_list.h"
struct sMmsMapping {
IedModel* model;
MmsDevice* mmsDevice;
MmsServer mmsServer;
LinkedList reportControls;
LinkedList gseControls;
LinkedList controlObjects;
LinkedList observedObjects;
LinkedList attributeAccessHandlers;
bool reportThreadRunning;
bool reportThreadFinished;
Thread reportWorkerThread;
IedServer iedServer;
IedConnectionIndicationHandler connectionIndicationHandler;
void* connectionIndicationHandlerParameter;
};
#endif /* MMS_MAPPING_INTERNAL_H_ */
/*
* mms_mapping_internal.h
*
* Copyright 2013 Michael Zillgith
*
* This file is part of libIEC61850.
*
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
#ifndef MMS_MAPPING_INTERNAL_H_
#define MMS_MAPPING_INTERNAL_H_
#include "hal_thread.h"
#include "linked_list.h"
struct sMmsMapping {
IedModel* model;
MmsDevice* mmsDevice;
MmsServer mmsServer;
LinkedList reportControls;
LinkedList gseControls;
LinkedList controlObjects;
LinkedList observedObjects;
LinkedList attributeAccessHandlers;
#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
LinkedList settingGroups;
#endif
#if (CONFIG_MMS_THREADLESS_STACK != 1)
bool reportThreadRunning;
bool reportThreadFinished;
Thread reportWorkerThread;
#endif
IedServer iedServer;
IedConnectionIndicationHandler connectionIndicationHandler;
void* connectionIndicationHandlerParameter;
};
#endif /* MMS_MAPPING_INTERNAL_H_ */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -1,453 +0,0 @@
/*
* iso_client_connection.c
*
* Client side representation of the ISO stack (COTP, session, presentation, ACSE)
*
* Copyright 2013 Michael Zillgith
*
* This file is part of libIEC61850.
*
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
#include "libiec61850_platform_includes.h"
#include "stack_config.h"
#include "socket.h"
#include "thread.h"
#include "cotp.h"
#include "iso_session.h"
#include "iso_presentation.h"
#include "iso_client_connection.h"
#include "acse.h"
#ifndef DEBUG_ISO_CLIENT
#ifdef DEBUG
#define DEBUG_ISO_CLIENT 1
#else
#define DEBUG_ISO_CLIENT 0
#endif /*DEBUG */
#endif /* DEBUG_ISO_SERVER */
#define STATE_IDLE 0
#define STATE_ASSOCIATED 1
#define STATE_ERROR 2
#define ISO_CLIENT_BUFFER_SIZE CONFIG_MMS_MAXIMUM_PDU_SIZE + 100
struct sIsoClientConnection
{
IsoIndicationCallback callback;
void* callbackParameter;
int state;
Socket socket;
CotpConnection* cotpConnection;
IsoPresentation* presentation;
IsoSession* session;
AcseConnection acseConnection;
uint8_t* sendBuffer; /* send buffer */
uint8_t* cotpBuf; /* receive buffer */
ByteBuffer* cotpBuffer;
ByteBuffer* transmitPayloadBuffer;
Semaphore transmitBufferMutex;
ByteBuffer* receivePayloadBuffer;
Semaphore receiveBufferMutex;
Thread thread;
};
static void*
connectionHandlingThread(void* threadParameter)
{
IsoClientConnection self = (IsoClientConnection) threadParameter;
IsoSessionIndication sessionIndication;
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT_CONNECTION: new connection\n");
Semaphore_wait(self->receiveBufferMutex);
while (CotpConnection_parseIncomingMessage(self->cotpConnection) == DATA_INDICATION) {
sessionIndication =
IsoSession_parseMessage(self->session,
CotpConnection_getPayload(self->cotpConnection));
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT_CONNECTION: parse message\n");
if (sessionIndication != SESSION_DATA) {
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT_CONNECTION: Invalid session message\n");
break;
}
if (!IsoPresentation_parseUserData(self->presentation, IsoSession_getUserData(self->session))) {
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT_CONNECTION: Invalid presentation message\n");
break;
}
self->callback(ISO_IND_DATA, self->callbackParameter,
&(self->presentation->nextPayload));
/* wait for user to release the buffer */
Semaphore_wait(self->receiveBufferMutex);
}
self->callback(ISO_IND_CLOSED, self->callbackParameter, NULL);
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT_CONNECTION: exit connection\n");
return NULL;
}
IsoClientConnection
IsoClientConnection_create(IsoIndicationCallback callback, void* callbackParameter)
{
IsoClientConnection self = (IsoClientConnection) calloc(1, sizeof(struct sIsoClientConnection));
self->callback = callback;
self->callbackParameter = callbackParameter;
self->state = STATE_IDLE;
self->sendBuffer = (uint8_t*) malloc(ISO_CLIENT_BUFFER_SIZE);
self->transmitPayloadBuffer = (ByteBuffer*) calloc(1, sizeof(ByteBuffer));
self->transmitPayloadBuffer->buffer = self->sendBuffer;
self->transmitPayloadBuffer->maxSize = ISO_CLIENT_BUFFER_SIZE;
self->receivePayloadBuffer = (ByteBuffer*) calloc(1, sizeof(ByteBuffer));
self->transmitBufferMutex = Semaphore_create(1);
self->receiveBufferMutex = Semaphore_create(1);
return self;
}
void
IsoClientConnection_sendMessage(IsoClientConnection self, ByteBuffer* payloadBuffer)
{
struct sBufferChain payloadBCMemory;
BufferChain payload = &payloadBCMemory;
BufferChain_init(payload, payloadBuffer->size, payloadBuffer->size, NULL, payloadBuffer->buffer);
struct sBufferChain presentationBCMemory;
BufferChain presentationBuffer = &presentationBCMemory;
presentationBuffer->buffer = self->sendBuffer + payload->length;
presentationBuffer->partMaxLength = ISO_CLIENT_BUFFER_SIZE;
IsoPresentation_createUserData(self->presentation, presentationBuffer, payload);
struct sBufferChain sessionBufferBCMemory;
BufferChain sessionBuffer = &sessionBufferBCMemory;
IsoSession_createDataSpdu(self->session, sessionBuffer, presentationBuffer);
CotpIndication indication = CotpConnection_sendDataMessage(self->cotpConnection, sessionBuffer);
/* release transmit buffer for use by API client */
Semaphore_post(self->transmitBufferMutex);
if (indication != OK)
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: IsoClientConnection_sendMessage: send message failed!\n");
}
void
IsoClientConnection_close(IsoClientConnection self)
{
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: IsoClientConnection_close\n");
if (self->socket != NULL) {
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: call socket close\n");
Socket_destroy(self->socket);
}
self->state = STATE_IDLE;
}
void
IsoClientConnection_destroy(IsoClientConnection self)
{
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: IsoClientConnection_destroy\n");
if (self->state == STATE_ASSOCIATED) {
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: call IsoClientConnection_close\n");
IsoClientConnection_close(self);
}
if (self->thread != NULL)
Thread_destroy(self->thread);
if (self->cotpBuf != NULL)
free(self->cotpBuf);
if (self->cotpBuffer != NULL)
free(self->cotpBuffer);
if (self->cotpConnection != NULL) {
CotpConnection_destroy(self->cotpConnection);
free(self->cotpConnection);
}
if (self->session != NULL)
free(self->session);
if (self->presentation != NULL)
free(self->presentation);
free(self->transmitPayloadBuffer);
free(self->receivePayloadBuffer);
Semaphore_destroy(self->receiveBufferMutex);
Semaphore_destroy(self->transmitBufferMutex);
free(self->sendBuffer);
free(self);
}
void
IsoClientConnection_abort(IsoClientConnection self)
{
//TODO block other messages from being sent
IsoClientConnection_allocateTransmitBuffer(self);
struct sBufferChain sAcseBuffer;
BufferChain acseBuffer = &sAcseBuffer;
acseBuffer->partMaxLength = ISO_CLIENT_BUFFER_SIZE;
acseBuffer->buffer = self->sendBuffer;
acseBuffer->nextPart = NULL;
AcseConnection_createAbortMessage(NULL, acseBuffer, false);
struct sBufferChain sPresentationBuffer;
BufferChain presentationBuffer = &sPresentationBuffer;
presentationBuffer->partMaxLength = ISO_CLIENT_BUFFER_SIZE - acseBuffer->length;
presentationBuffer->buffer = self->sendBuffer + acseBuffer->length;
presentationBuffer->nextPart = acseBuffer;
IsoPresentation_createAbortUserMessage(self->presentation, presentationBuffer, acseBuffer);
struct sBufferChain sSessionBuffer;
BufferChain sessionBuffer = &sSessionBuffer;
sessionBuffer->partMaxLength = ISO_CLIENT_BUFFER_SIZE - presentationBuffer->length;
sessionBuffer->buffer = self->sendBuffer + presentationBuffer->length;
sessionBuffer->nextPart = presentationBuffer;
IsoSession_createAbortSpdu(self->session, sessionBuffer, presentationBuffer);
CotpConnection_sendDataMessage(self->cotpConnection, sessionBuffer);
Semaphore_post(self->transmitBufferMutex);
}
void
IsoClientConnection_release(IsoClientConnection self)
{
//TODO block other messages from being sent
IsoClientConnection_allocateTransmitBuffer(self);
struct sBufferChain sAcseBuffer;
BufferChain acseBuffer = &sAcseBuffer;
acseBuffer->partMaxLength = ISO_CLIENT_BUFFER_SIZE;
acseBuffer->buffer = self->sendBuffer;
acseBuffer->nextPart = NULL;
AcseConnection_createReleaseRequestMessage(NULL, acseBuffer);
struct sBufferChain sPresentationBuffer;
BufferChain presentationBuffer = &sPresentationBuffer;
presentationBuffer->partMaxLength = ISO_CLIENT_BUFFER_SIZE - acseBuffer->length;
presentationBuffer->buffer = self->sendBuffer + acseBuffer->length;
presentationBuffer->nextPart = acseBuffer;
IsoPresentation_createUserDataACSE(self->presentation, presentationBuffer, acseBuffer);
struct sBufferChain sSessionBuffer;
BufferChain sessionBuffer = &sSessionBuffer;
sessionBuffer->partMaxLength = ISO_CLIENT_BUFFER_SIZE - presentationBuffer->length;
sessionBuffer->buffer = self->sendBuffer + presentationBuffer->length;
sessionBuffer->nextPart = presentationBuffer;
IsoSession_createFinishSpdu(NULL, sessionBuffer, presentationBuffer);
CotpConnection_sendDataMessage(self->cotpConnection, sessionBuffer);
Semaphore_post(self->transmitBufferMutex);
}
void
IsoClientConnection_associate(IsoClientConnection self, IsoConnectionParameters params,
ByteBuffer* payload)
{
Socket socket = TcpSocket_create();
self->socket = socket;
if (!Socket_connect(socket, params->hostname, params->tcpPort))
goto returnError;
self->cotpBuf = (uint8_t*) malloc(ISO_CLIENT_BUFFER_SIZE);
self->cotpBuffer = (ByteBuffer*) calloc(1, sizeof(ByteBuffer));
ByteBuffer_wrap(self->cotpBuffer, self->cotpBuf, 0, ISO_CLIENT_BUFFER_SIZE);
self->cotpConnection = (CotpConnection*) calloc(1, sizeof(CotpConnection));
CotpConnection_init(self->cotpConnection, socket, self->cotpBuffer);
/* COTP (ISO transport) handshake */
CotpIndication cotpIndication =
CotpConnection_sendConnectionRequestMessage(self->cotpConnection, params);
cotpIndication = CotpConnection_parseIncomingMessage(self->cotpConnection);
if (cotpIndication != CONNECT_INDICATION)
goto returnError;
/* Upper layers handshake */
struct sBufferChain sAcsePayload;
BufferChain acsePayload = &sAcsePayload;
acsePayload->buffer = payload->buffer;
acsePayload->partLength = payload->size;
acsePayload->length = payload->size;
acsePayload->nextPart = NULL;
AcseConnection_init(&(self->acseConnection), NULL, NULL);
AcseAuthenticationParameter authParameter = NULL;
if (params != NULL)
authParameter = params->acseAuthParameter;
struct sBufferChain sAcseBuffer;
BufferChain acseBuffer = &sAcseBuffer;
acseBuffer->buffer = self->sendBuffer + payload->size;
acseBuffer->partMaxLength = ISO_CLIENT_BUFFER_SIZE - acsePayload->length;
AcseConnection_createAssociateRequestMessage(&(self->acseConnection), params, acseBuffer, acsePayload,
authParameter);
struct sBufferChain sPresentationBuffer;
BufferChain presentationBuffer = &sPresentationBuffer;
presentationBuffer->buffer = self->sendBuffer + acseBuffer->length;
presentationBuffer->partMaxLength = ISO_CLIENT_BUFFER_SIZE - acseBuffer->length;
self->presentation = (IsoPresentation*) calloc(1, sizeof(IsoPresentation));
IsoPresentation_init(self->presentation);
IsoPresentation_createConnectPdu(self->presentation, params, presentationBuffer, acseBuffer);
struct sBufferChain sSessionBuffer;
BufferChain sessionBuffer = &sSessionBuffer;
sessionBuffer->buffer = self->sendBuffer + presentationBuffer->length;
self->session = (IsoSession*) calloc(1, sizeof(IsoSession));
IsoSession_init(self->session);
IsoSession_createConnectSpdu(self->session, params, sessionBuffer,
presentationBuffer);
CotpConnection_sendDataMessage(self->cotpConnection, sessionBuffer);
Semaphore_post(self->transmitBufferMutex);
cotpIndication = CotpConnection_parseIncomingMessage(self->cotpConnection);
if (cotpIndication != DATA_INDICATION)
goto returnError;
IsoSessionIndication sessionIndication;
sessionIndication =
IsoSession_parseMessage(self->session, CotpConnection_getPayload(self->cotpConnection));
if (sessionIndication != SESSION_CONNECT) {
if (DEBUG_ISO_CLIENT)
printf("IsoClientConnection_associate: no session connect indication\n");
goto returnError;
}
if (!IsoPresentation_parseAcceptMessage(self->presentation, IsoSession_getUserData(self->session))) {
if (DEBUG_ISO_CLIENT)
printf("IsoClientConnection_associate: no presentation ok indication\n");
goto returnError;
}
AcseIndication acseIndication;
acseIndication = AcseConnection_parseMessage(&(self->acseConnection), &self->presentation->nextPayload);
if (acseIndication != ACSE_ASSOCIATE) {
if (DEBUG_ISO_CLIENT)
printf("IsoClientConnection_associate: no ACSE_ASSOCIATE indication\n");
goto returnError;
}
ByteBuffer_wrap(self->receivePayloadBuffer, self->acseConnection.userDataBuffer,
self->acseConnection.userDataBufferSize, self->acseConnection.userDataBufferSize);
Semaphore_wait(self->receiveBufferMutex);
self->callback(ISO_IND_ASSOCIATION_SUCCESS, self->callbackParameter, self->receivePayloadBuffer);
self->state = STATE_ASSOCIATED;
self->thread = Thread_create(connectionHandlingThread, self, false);
Thread_start(self->thread);
return;
returnError:
self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL);
self->state = STATE_ERROR;
Socket_destroy(self->socket);
self->socket = NULL;
return;
}
ByteBuffer*
IsoClientConnection_allocateTransmitBuffer(IsoClientConnection self)
{
Semaphore_wait(self->transmitBufferMutex);
self->transmitPayloadBuffer->size = 0;
self->transmitPayloadBuffer->maxSize = ISO_CLIENT_BUFFER_SIZE;
return self->transmitPayloadBuffer;
}
void
IsoClientConnection_releaseReceiveBuffer(IsoClientConnection self)
{
Semaphore_post(self->receiveBufferMutex);
}
Loading…
Cancel
Save