Merge branch 'v1.6_develop' into v1.6_develop_rgoose_sntp__merge_develop

pull/521/head
Michael Zillgith 1 year ago
commit 625fb65e50

@ -170,7 +170,7 @@
/* allow application to set server identity (for MMS identity service) at runtime */
#define CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY 1
/* Force memory alignment - required for some platforms (required more memory for buffered reporting) */
/* Force memory alignment - required for some platforms (requires more memory for buffered reporting) */
#define CONFIG_IEC61850_FORCE_MEMORY_ALIGNMENT 1
/* compile with support for R-GOOSE (mbedtls requried) */
@ -249,10 +249,24 @@
/* enable to configure MmsServer at runtime */
#define CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME 1
/* Define the default number of the maximum outstanding calls allowed by the caller (client) */
#define CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING 5
/* Define the default number of the maximum outstanding calls allowed by the calling endpoint (server) */
#define CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED 5
/************************************************************************************
* Check configuration for consistency - DO NOT MODIFY THIS PART!
************************************************************************************/
#if (CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING < 1)
#error "Invalid configuration: CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING must be greater than 0!"
#endif
#if (CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED < 1)
#error "Invalid configuration: CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED must be greater than 0!"
#endif
#if (MMS_JOURNAL_SERVICE != 1)
#if (CONFIG_IEC61850_LOG_SERVICE == 1)

@ -169,7 +169,7 @@
/* allow application to set server identity (for MMS identity service) at runtime */
#define CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY 1
/* Force memory alignment - required for some platforms (required more memory for buffered reporting) */
/* Force memory alignment - required for some platforms (requires more memory for buffered reporting) */
#define CONFIG_IEC61850_FORCE_MEMORY_ALIGNMENT 1
/* default results for MMS identify service */
@ -238,10 +238,24 @@
/* enable to configure MmsServer at runtime */
#define CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME 1
/* Define the default number of the maximum outstanding calls allowed by the caller (client) */
#define CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING 5
/* Define the default number of the maximum outstanding calls allowed by the calling endpoint (server) */
#define CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED 5
/************************************************************************************
* Check configuration for consistency - DO NOT MODIFY THIS PART!
************************************************************************************/
#if (CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING < 1)
#error "Invalid configuration: CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING must be greater than 0!"
#endif
#if (CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED < 1)
#error "Invalid configuration: CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED must be greater than 0!"
#endif
#if (MMS_JOURNAL_SERVICE != 1)
#if (CONFIG_IEC61850_LOG_SERVICE == 1)

@ -431,6 +431,9 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32 IedConnection_getRequestTimeout(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_setMaxOutstandingCalls(IntPtr self, int calling, int called);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_setTimeQuality(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool leapSecondKnown, [MarshalAs(UnmanagedType.I1)] bool clockFailure, [MarshalAs(UnmanagedType.I1)] bool clockNotSynchronized, int subsecondPrecision);
@ -559,6 +562,12 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_installStateChangedHandler(IntPtr connection, InternalStateChangedHandler handler, IntPtr parameter);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedServer_ignoreReadAccess(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool ignore);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedServer_ignoreClientRequests(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool ignore);
/*********************
* Async functions
*********************/
@ -815,6 +824,16 @@ namespace IEC61850
}
}
/// <summary>
/// Set the maximum number outstanding calls allowed for this connection
/// </summary>
/// <param name="calling">the maximum outstanding calls allowed by the caller (client)</param>
/// <param name="called">the maximum outstanding calls allowed by the called endpoint (server)</param>
public void SetMaxOutstandingCalls(int calling, int called)
{
IedConnection_setMaxOutstandingCalls(connection, calling, called);
}
/// <summary>
/// Gets or sets the maximum size if a PDU (has to be set before calling connect!).
/// </summary>
@ -1175,6 +1194,9 @@ namespace IEC61850
private static List<MmsJournalEntry> WrapNativeLogQueryResult(IntPtr linkedList)
{
if (linkedList == IntPtr.Zero)
return null;
List<MmsJournalEntry> journalEntries = new List<MmsJournalEntry>();
IntPtr element = LinkedList_getNext(linkedList);
@ -1945,6 +1967,24 @@ namespace IEC61850
}
}
/// <summary>
/// Ignore all MMS requests from clients (for testing purposes)
/// </summary>
/// <param name="ignore">when true all requests from clients will be ignored</param>
public void IgnoreClientRequests(bool ignore)
{
IedServer_ignoreClientRequests(connection, ignore);
}
/// <summary>
/// Temporarily ignore read requests (for testing purposes)
/// </summary>
/// <param name="ignore">true to ignore read requests, false to handle read requests.</param>
public void IgnoreReadAccess(bool ignore)
{
IedServer_ignoreReadAccess(connection, ignore);
}
/// <summary>
/// Read the values of a data set (GetDataSetValues service).
/// </summary>
@ -2238,22 +2278,27 @@ namespace IEC61850
GetDataSetDirectoryHandler handler = callbackInfo.Item1;
object handlerParameter = callbackInfo.Item2;
IntPtr element = LinkedList_getNext(dataSetDirectory);
handle.Free();
List<string> newList = new List<string>();
List<string> newList = null;
while (element != IntPtr.Zero)
if (dataSetDirectory != IntPtr.Zero)
{
string dataObject = Marshal.PtrToStringAnsi(LinkedList_getData(element));
newList = new List<string>();
newList.Add(dataObject);
IntPtr element = LinkedList_getNext(dataSetDirectory);
element = LinkedList_getNext(element);
}
while (element != IntPtr.Zero)
{
string dataObject = Marshal.PtrToStringAnsi(LinkedList_getData(element));
LinkedList_destroy(dataSetDirectory);
newList.Add(dataObject);
element = LinkedList_getNext(element);
}
LinkedList_destroy(dataSetDirectory);
}
handler.Invoke(invokeId, handlerParameter, (IedClientError)err, newList, isDeletable);
}
@ -2428,11 +2473,9 @@ namespace IEC61850
dataSet = new DataSet(nativeDataSet);
}
handler(invokeId, handlerParameter, clientError, dataSet);
}
public delegate void ReadDataSetHandler(UInt32 invokeId,object parameter,IedClientError err,DataSet dataSet);
/// <summary>
@ -2566,7 +2609,6 @@ namespace IEC61850
{
handler(invokeId, handlerParameter, clientError, null, moreFollows);
}
}
/// <summary>
@ -2632,7 +2674,6 @@ namespace IEC61850
return GetLogicalDeviceDataSetsAsync(null, ldName, continueAfter, handler, parameter);
}
public UInt32 GetLogicalDeviceDataSetsAsync(List<string> result, string ldName, string continueAfter, GetNameListHandler handler, object parameter)
{
int error;
@ -2977,6 +3018,19 @@ namespace IEC61850
IED_STATE_CLOSING = 3
}
public static class IedClientErrorExtension
{
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedClientError_toString(int err);
public static string ToString(this IedClientError err)
{
string stringVal = Marshal.PtrToStringAnsi(IedClientError_toString((int)err));
return stringVal;
}
}
/// <summary>
/// Error codes for client side functions
/// </summary>
@ -3062,6 +3116,9 @@ namespace IEC61850
/** Received an invalid response message from the server */
IED_ERROR_MALFORMED_MESSAGE = 34,
/** Service was not executed because required resource is still in use */
IED_ERROR_OBJECT_CONSTRAINT_CONFLICT = 35,
/** Service not implemented */
IED_ERROR_SERVICE_NOT_IMPLEMENTED = 98,

@ -1,7 +1,7 @@
/*
* IEC61850CommonAPI.cs
*
* Copyright 2014-2017 Michael Zillgith
* Copyright 2014-2024 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -454,6 +454,9 @@ namespace IEC61850
SetByMmsUtcTime (mmsUtcTime);
}
/// <summary>
/// Initializes a new instance of the <see cref="IEC61850CommonAPI.Timestamp"/> class.
/// </summary>
public Timestamp()
{
self = Timestamp_create ();
@ -461,6 +464,19 @@ namespace IEC61850
responsibleForDeletion = true;
}
/// <summary>
/// Initializes a new instance of the <see cref="IEC61850CommonAPI.Timestamp"/> class.
/// </summary>
public Timestamp(Timestamp other) : this()
{
SetTimeInSeconds (other.GetTimeInSeconds ());
SetSubsecondPrecision (other.GetSubsecondPrecision ());
SetFractionOfSecondPart (other.GetFractionOfSecondPart ());
SetLeapSecondKnow(other.IsLeapSecondKnown());
SetClockFailure(other.HasClockFailure());
SetClockNotSynchronized(other.IsClockNotSynchronized());
}
public Timestamp(byte[] value)
{
self = Timestamp_createFromByteArray (value);

@ -1874,6 +1874,9 @@ namespace IEC61850
[return: MarshalAs(UnmanagedType.I1)]
static extern bool ControlAction_getInterlockCheck(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ControlAction_getT(IntPtr self);
private IntPtr self;
private IedServer.ControlHandlerInfo info;
private IedServer iedServer;
@ -2003,6 +2006,18 @@ namespace IEC61850
{
return ControlAction_getInterlockCheck(self);
}
/// <summary>
/// Gets the time (paramter T) of the control action
/// </summary>
public Timestamp GetT()
{
IntPtr tPtr = ControlAction_getT(self);
Timestamp t = new Timestamp(tPtr, false);
return new Timestamp(t);
}
}
public delegate void GoCBEventHandler(MmsGooseControlBlock goCB, int cbEvent, object parameter);
@ -2247,6 +2262,10 @@ namespace IEC61850
static extern void IedServer_handleWriteAccessForComplexAttribute(IntPtr self, IntPtr dataAttribute,
InternalWriteAccessHandler handler, IntPtr parameter);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedServer_handleWriteAccessForDataObject(IntPtr self, IntPtr dataObject, int fc,
InternalWriteAccessHandler handler, IntPtr parameter);
public delegate void ConnectionIndicationHandler(IedServer iedServer, ClientConnection clientConnection, bool connected, object parameter);
private ConnectionIndicationHandler connectionHandler = null;
@ -2713,12 +2732,27 @@ namespace IEC61850
}
}
private void AddHandlerInfoForDataObjectRecursive(DataObject dataObject, FunctionalConstraint fc, WriteAccessHandler handler, object parameter, InternalWriteAccessHandler internalHandler)
{
foreach (ModelNode child in dataObject.GetChildren())
{
if (child is DataAttribute && (child as DataAttribute).FC == fc)
{
AddHandlerInfoForDataAttributeRecursive(child as DataAttribute, handler, parameter, internalHandler);
}
else if (child is DataObject)
{
AddHandlerInfoForDataObjectRecursive(child as DataObject, fc, handler, parameter, internalHandler);
}
}
}
/// <summary>
/// Install a WriteAccessHandler for a data attribute and for all sub data attributes
/// </summary>
/// This instructs the server to monitor write attempts by MMS clients to specific
/// data attributes.If a client tries to write to the monitored data attribute the
/// handler is invoked.The handler can decide if the write access will be allowed
/// data attributes. If a client tries to write to the monitored data attribute the
/// handler is invoked. The handler can decide if the write access will be allowed
/// or denied.If a WriteAccessHandler is set for a specific data attribute - the
/// default write access policy will not be performed for that data attribute.
/// <remarks>
@ -2738,6 +2772,27 @@ namespace IEC61850
IedServer_handleWriteAccessForComplexAttribute(self, dataAttr.self, internalHandler, IntPtr.Zero);
}
/// <summary>
/// Install a WriteAccessHandler for a data object and for all sub data objects and sub data attributes that have the same functional constraint
/// </summary>
/// This instructs the server to monitor write attempts by MMS clients to specific
/// data attributes. If a client tries to write to the monitored data attribute the
/// handler is invoked. The handler can decide if the write access will be allowed
/// or denied. If a WriteAccessHandler is set the
/// default write access policy will not be performed for the matching data attributes.
/// <param name="dataObject">the data object to monitor</param>
/// <param name="fc">the functional constraint (FC) to monitor</param>
/// <param name="handler">the callback function that is invoked if a client tries to write to a monitored data attribute that is a child of the data object.</param>
/// <param name="parameter">a user provided parameter that is passed to the WriteAccessHandler when called.</param>
public void HandleWriteAccessForDataObject(DataObject dataObj, FunctionalConstraint fc, WriteAccessHandler handler, object parameter)
{
InternalWriteAccessHandler internalHandler = new InternalWriteAccessHandler(WriteAccessHandlerImpl);
AddHandlerInfoForDataObjectRecursive(dataObj, fc, handler, parameter, internalHandler);
IedServer_handleWriteAccessForDataObject(self, dataObj.self, (int)fc, internalHandler, IntPtr.Zero);
}
/// <summary>
/// Set the defualt write access policy for a specific FC. The default policy is applied when no handler is installed for a data attribute
/// </summary>

@ -36,7 +36,7 @@ namespace example2
}
catch (IedConnectionException e)
{
Console.WriteLine("IED connection excepion: " + e.Message);
Console.WriteLine("IED connection exception: " + e.Message + " err: " + e.GetIedClientError().ToString());
}
// release all resources - do NOT use the object after this call!!

@ -1,7 +1,7 @@
/*
* ethernet_linux.c
*
* Copyright 2013-2022 Michael Zillgith
* Copyright 2013-2024 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -57,7 +57,8 @@ EthernetHandleSet_new(void)
{
EthernetHandleSet result = (EthernetHandleSet) GLOBAL_MALLOC(sizeof(struct sEthernetHandleSet));
if (result != NULL) {
if (result != NULL)
{
result->handles = NULL;
result->nhandles = 0;
}
@ -68,8 +69,8 @@ EthernetHandleSet_new(void)
void
EthernetHandleSet_addSocket(EthernetHandleSet self, const EthernetSocket sock)
{
if (self != NULL && sock != NULL) {
if (self != NULL && sock != NULL)
{
int i = self->nhandles++;
self->handles = realloc(self->handles, self->nhandles * sizeof(struct pollfd));
@ -82,12 +83,14 @@ EthernetHandleSet_addSocket(EthernetHandleSet self, const EthernetSocket sock)
void
EthernetHandleSet_removeSocket(EthernetHandleSet self, const EthernetSocket sock)
{
if ((self != NULL) && (sock != NULL)) {
if ((self != NULL) && (sock != NULL))
{
int i;
for (i = 0; i < self->nhandles; i++) {
if (self->handles[i].fd == sock->rawSocket) {
for (i = 0; i < self->nhandles; i++)
{
if (self->handles[i].fd == sock->rawSocket)
{
memmove(&self->handles[i], &self->handles[i+1], sizeof(struct pollfd) * (self->nhandles - i - 1));
self->nhandles--;
return;
@ -128,7 +131,8 @@ getInterfaceIndex(int sock, const char* deviceName)
strncpy(ifr.ifr_name, deviceName, IFNAMSIZ - 1);
if (ioctl(sock, SIOCGIFINDEX, &ifr) == -1) {
if (ioctl(sock, SIOCGIFINDEX, &ifr) == -1)
{
if (DEBUG_SOCKET)
printf("ETHERNET_LINUX: Failed to get interface index");
return -1;
@ -157,7 +161,7 @@ Ethernet_getInterfaceMACAddress(const char* interfaceId, uint8_t* addr)
int i;
for(i = 0; i < 6; i++ )
for (i = 0; i < 6; i++ )
{
addr[i] = (unsigned char)buffer.ifr_hwaddr.sa_data[i];
}
@ -166,106 +170,112 @@ Ethernet_getInterfaceMACAddress(const char* interfaceId, uint8_t* addr)
EthernetSocket
Ethernet_createSocket(const char* interfaceId, uint8_t* destAddress)
{
EthernetSocket ethernetSocket = GLOBAL_CALLOC(1, sizeof(struct sEthernetSocket));
EthernetSocket self = GLOBAL_CALLOC(1, sizeof(struct sEthernetSocket));
if (ethernetSocket) {
ethernetSocket->rawSocket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (self)
{
self->rawSocket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (ethernetSocket->rawSocket == -1) {
if (self->rawSocket == -1)
{
if (DEBUG_SOCKET)
printf("Error creating raw socket!\n");
GLOBAL_FREEMEM(ethernetSocket);
GLOBAL_FREEMEM(self);
return NULL;
}
ethernetSocket->socketAddress.sll_family = PF_PACKET;
ethernetSocket->socketAddress.sll_protocol = htons(ETH_P_ALL);
self->socketAddress.sll_family = PF_PACKET;
self->socketAddress.sll_protocol = htons(ETH_P_ALL);
int ifcIdx = getInterfaceIndex(ethernetSocket->rawSocket, interfaceId);
int ifcIdx = getInterfaceIndex(self->rawSocket, interfaceId);
if (ifcIdx == -1) {
Ethernet_destroySocket(ethernetSocket);
if (ifcIdx == -1)
{
Ethernet_destroySocket(self);
return NULL;
}
ethernetSocket->socketAddress.sll_ifindex = ifcIdx;
self->socketAddress.sll_ifindex = ifcIdx;
ethernetSocket->socketAddress.sll_hatype = ARPHRD_ETHER;
ethernetSocket->socketAddress.sll_pkttype = PACKET_HOST | PACKET_MULTICAST;
self->socketAddress.sll_hatype = ARPHRD_ETHER;
self->socketAddress.sll_pkttype = PACKET_HOST | PACKET_MULTICAST;
ethernetSocket->socketAddress.sll_halen = ETH_ALEN;
self->socketAddress.sll_halen = ETH_ALEN;
memset(ethernetSocket->socketAddress.sll_addr, 0, 8);
memset(self->socketAddress.sll_addr, 0, 8);
if (destAddress != NULL)
memcpy(ethernetSocket->socketAddress.sll_addr, destAddress, 6);
memcpy(self->socketAddress.sll_addr, destAddress, 6);
self->isBind = false;
ethernetSocket->isBind = false;
Ethernet_setMode(self, ETHERNET_SOCKET_MODE_PROMISC);
}
return ethernetSocket;
return self;
}
void
Ethernet_setMode(EthernetSocket ethSocket, EthernetSocketMode mode)
Ethernet_setMode(EthernetSocket self, EthernetSocketMode mode)
{
if (ethSocket) {
if (mode == ETHERNET_SOCKET_MODE_PROMISC) {
if (self)
{
if (mode == ETHERNET_SOCKET_MODE_PROMISC)
{
struct ifreq ifr;
if (ioctl (ethSocket->rawSocket, SIOCGIFFLAGS, &ifr) == -1)
if (ioctl (self->rawSocket, SIOCGIFFLAGS, &ifr) == -1)
{
if (DEBUG_SOCKET)
printf("ETHERNET_LINUX: Problem getting device flags");
return;
}
ifr.ifr_flags |= IFF_PROMISC;
if (ioctl (ethSocket->rawSocket, SIOCSIFFLAGS, &ifr) == -1)
if (ioctl (self->rawSocket, SIOCSIFFLAGS, &ifr) == -1)
{
if (DEBUG_SOCKET)
printf("ETHERNET_LINUX: Setting device to promiscuous mode failed");
return;
}
}
else if (mode == ETHERNET_SOCKET_MODE_ALL_MULTICAST) {
else if (mode == ETHERNET_SOCKET_MODE_ALL_MULTICAST)
{
struct ifreq ifr;
if (ioctl (ethSocket->rawSocket, SIOCGIFFLAGS, &ifr) == -1)
if (ioctl (self->rawSocket, SIOCGIFFLAGS, &ifr) == -1)
{
if (DEBUG_SOCKET)
printf("ETHERNET_LINUX: Problem getting device flags");
return;
}
ifr.ifr_flags |= IFF_ALLMULTI;
if (ioctl (ethSocket->rawSocket, SIOCSIFFLAGS, &ifr) == -1)
if (ioctl (self->rawSocket, SIOCSIFFLAGS, &ifr) == -1)
{
if (DEBUG_SOCKET)
printf("ETHERNET_LINUX: Setting device to promiscuous mode failed");
return;
}
}
else if (mode == ETHERNET_SOCKET_MODE_HOST_ONLY) {
ethSocket->socketAddress.sll_pkttype = PACKET_HOST;
else if (mode == ETHERNET_SOCKET_MODE_HOST_ONLY)
{
self->socketAddress.sll_pkttype = PACKET_HOST;
}
else if (mode == ETHERNET_SOCKET_MODE_MULTICAST) {
ethSocket->socketAddress.sll_pkttype = PACKET_HOST | PACKET_MULTICAST;
else if (mode == ETHERNET_SOCKET_MODE_MULTICAST)
{
self->socketAddress.sll_pkttype = PACKET_HOST | PACKET_MULTICAST;
}
}
}
void
Ethernet_addMulticastAddress(EthernetSocket ethSocket, uint8_t* multicastAddress)
Ethernet_addMulticastAddress(EthernetSocket self, uint8_t* multicastAddress)
{
struct packet_mreq mreq;
memset(&mreq, 0, sizeof(struct packet_mreq));
mreq.mr_ifindex = ethSocket->socketAddress.sll_ifindex;
mreq.mr_ifindex = self->socketAddress.sll_ifindex;
mreq.mr_alen = ETH_ALEN;
mreq.mr_type = PACKET_MR_MULTICAST;
mreq.mr_address[0] = multicastAddress[0];
@ -275,17 +285,17 @@ Ethernet_addMulticastAddress(EthernetSocket ethSocket, uint8_t* multicastAddress
mreq.mr_address[4] = multicastAddress[4];
mreq.mr_address[5] = multicastAddress[5];
int res = setsockopt(ethSocket->rawSocket, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
int res = setsockopt(self->rawSocket, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
if (res != 0) {
if (res != 0)
{
if (DEBUG_SOCKET)
printf("ETHERNET_LINUX: Setting multicast address failed");
}
}
void
Ethernet_setProtocolFilter(EthernetSocket ethSocket, uint16_t etherType)
Ethernet_setProtocolFilter(EthernetSocket self, uint16_t etherType)
{
if (etherType == 0x88b8)
{
@ -303,22 +313,22 @@ Ethernet_setProtocolFilter(EthernetSocket ethSocket, uint16_t etherType)
fprog.len = sizeof(filter) / sizeof(*filter);
fprog.filter = filter;
if (setsockopt(ethSocket->rawSocket, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) == -1)
if (setsockopt(self->rawSocket, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) == -1)
if (DEBUG_SOCKET)
printf("ETHERNET_LINUX: Applying filter failed");
}
else
{
ethSocket->socketAddress.sll_protocol = htons(etherType);
self->socketAddress.sll_protocol = htons(etherType);
}
}
/* non-blocking receive */
int
Ethernet_receivePacket(EthernetSocket self, uint8_t* buffer, int bufferSize)
{
if (self->isBind == false) {
if (self->isBind == false)
{
if (bind(self->rawSocket, (struct sockaddr*) &self->socketAddress, sizeof(self->socketAddress)) == 0)
self->isBind = true;
else
@ -329,17 +339,20 @@ Ethernet_receivePacket(EthernetSocket self, uint8_t* buffer, int bufferSize)
}
void
Ethernet_sendPacket(EthernetSocket ethSocket, uint8_t* buffer, int packetSize)
Ethernet_sendPacket(EthernetSocket self, uint8_t* buffer, int packetSize)
{
sendto(ethSocket->rawSocket, buffer, packetSize,
0, (struct sockaddr*) &(ethSocket->socketAddress), sizeof(ethSocket->socketAddress));
sendto(self->rawSocket, buffer, packetSize,
0, (struct sockaddr*) &(self->socketAddress), sizeof(self->socketAddress));
}
void
Ethernet_destroySocket(EthernetSocket ethSocket)
Ethernet_destroySocket(EthernetSocket self)
{
close(ethSocket->rawSocket);
GLOBAL_FREEMEM(ethSocket);
if (self)
{
close(self->rawSocket);
GLOBAL_FREEMEM(self);
}
}
bool
@ -347,4 +360,3 @@ Ethernet_isSupported()
{
return true;
}

@ -1,7 +1,7 @@
/*
* socket_linux.c
*
* Copyright 2013-2021 Michael Zillgith
* Copyright 2013-2024 Michael Zillgith
*
* This file is part of Platform Abstraction Layer (libpal)
* for libiec61850, libmms, and lib60870.
@ -907,7 +907,7 @@ UdpSocket_receiveFrom(UdpSocket self, char* address, int maxAddrSize, uint8_t* m
{
//TODO add support for IPv6
struct sockaddr_storage remoteAddress;
memset(&remoteAddress, 0, sizeof(struct sockaddr_storage));
socklen_t structSize = sizeof(struct sockaddr_storage);
int result = recvfrom(self->fd, msg, msgSize, MSG_DONTWAIT, (struct sockaddr*)&remoteAddress, &structSize);

@ -843,6 +843,7 @@ UdpSocket_receiveFrom(UdpSocket self, char* address, int maxAddrSize, uint8_t* m
{
//TODO add support for IPv6
struct sockaddr_storage remoteAddress;
memset(&remoteAddress, 0, sizeof(struct sockaddr_storage));
socklen_t structSize = sizeof(struct sockaddr_storage);
if (address)

@ -37,9 +37,9 @@
#endif
#if (CONFIG_DEBUG_TLS == 1)
#define DEBUG_PRINT(appId, fmt, ...) fprintf(stderr, "%s: " fmt, appId, ## __VA_ARGS__);
#define DEBUG_PRINT(appId, fmt, ...) fprintf(stderr, "%s: " fmt, appId, ## __VA_ARGS__)
#else
#define DEBUG_PRINT(fmt, ...) {do {} while(0);}
#define DEBUG_PRINT(fmt, ...) do {} while(0)
#endif
@ -265,7 +265,7 @@ TLSConfiguration_create()
mbedtls_ssl_conf_authmode(&(self->conf), MBEDTLS_SSL_VERIFY_REQUIRED);
mbedtls_ssl_conf_renegotiation(&(self->conf), MBEDTLS_SSL_LEGACY_NO_RENEGOTIATION);
mbedtls_ssl_conf_renegotiation(&(self->conf), MBEDTLS_SSL_RENEGOTIATION_ENABLED);
/* static int hashes[] = {3,4,5,6,7,8,0}; */
/* mbedtls_ssl_conf_sig_hashes(&(self->conf), hashes); */
@ -297,7 +297,7 @@ TLSConfiguration_create()
void
TLSConfiguration_setClientMode(TLSConfiguration self)
{
self->conf.endpoint = MBEDTLS_SSL_IS_CLIENT;
mbedtls_ssl_conf_endpoint(&(self->conf), MBEDTLS_SSL_IS_CLIENT);
}
void
@ -852,20 +852,26 @@ TLSSocket_performHandshake(TLSSocket self)
{
int ret = mbedtls_ssl_renegotiate(&(self->ssl));
if (ret == 0) {
if (ret == 0 || ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE ||
ret == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS || ret == MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS) {
if (getTLSVersion(self->ssl.major_ver, self->ssl.minor_ver) < TLS_VERSION_TLS_1_2) {
raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_WRN_INSECURE_TLS_VERSION, "Warning: Insecure TLS version", self);
}
DEBUG_PRINT("TLS", "TLSSocket_performHandshake Success -> ret=%i\n", ret);
raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_INFO, TLS_EVENT_CODE_INF_SESSION_RENEGOTIATION, "TLS session renegotiation completed", self);
return true;
}
else {
DEBUG_PRINT("TLS", "TLSSocket_performHandshake failed -> ret=%i\n", ret);
if (self->tlsConfig->eventHandler) {
uint32_t flags = mbedtls_ssl_get_verify_result(&(self->ssl));
raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_INF_SESSION_RENEGOTIATION, "Alarm: TLS session renegotiation failed", self);
createSecurityEvents(self->tlsConfig, ret, flags, self);
/* mbedtls_ssl_renegotiate mandates to reset the ssl session in case of errors */
ret = mbedtls_ssl_session_reset(&(self->ssl));
if (ret != 0) {
DEBUG_PRINT("TLS", "mbedtls_ssl_session_reset failed -> ret: -0x%X\n", -ret);
}
return false;
@ -898,16 +904,10 @@ startRenegotiationIfRequired(TLSSocket self)
if (Hal_getTimeInMs() <= self->lastRenegotiationTime + self->tlsConfig->renegotiationTimeInMs)
return true;
raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_INFO, TLS_EVENT_CODE_INF_SESSION_RENEGOTIATION, "Info: session renegotiation started", self);
if (TLSSocket_performHandshake(self) == false) {
DEBUG_PRINT("TLS", " renegotiation failed\n");
if (TLSSocket_performHandshake(self) == false)
return false;
}
DEBUG_PRINT("TLS", " started renegotiation\n");
self->lastRenegotiationTime = Hal_getTimeInMs();
return true;
}
@ -920,22 +920,30 @@ TLSSocket_read(TLSSocket self, uint8_t* buf, int size)
return -1;
}
int ret = mbedtls_ssl_read(&(self->ssl), buf, size);
if ((ret == MBEDTLS_ERR_SSL_WANT_READ) || (ret == MBEDTLS_ERR_SSL_WANT_WRITE))
return 0;
int len = 0;
while (len < size) {
int ret = mbedtls_ssl_read(&(self->ssl), (buf + len), (size - len));
if (ret > 0) {
len += ret;
continue;
}
if (ret < 0) {
switch (ret) {
case 0: // falling through
case MBEDTLS_ERR_SSL_WANT_READ:
case MBEDTLS_ERR_SSL_WANT_WRITE:
case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS:
case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS:
// Known "good" cases indicating the read is done
return len;
switch (ret)
{
case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
DEBUG_PRINT("TLS", " connection was closed gracefully\n");
return -1;
break;
case MBEDTLS_ERR_NET_CONN_RESET:
DEBUG_PRINT("TLS", " connection was reset by peer\n");
return -1;
break;
default:
DEBUG_PRINT("TLS", " mbedtls_ssl_read returned -0x%x\n", -ret);
@ -945,19 +953,23 @@ TLSSocket_read(TLSSocket self, uint8_t* buf, int size)
createSecurityEvents(self->tlsConfig, ret, flags, self);
}
}
return -1;
int reset_err = mbedtls_ssl_session_reset(&(self->ssl));
if (0 != reset_err) {
DEBUG_PRINT("TLS", "mbedtls_ssl_session_reset failed -0x%X\n", -reset_err);
}
return ret;
}
return ret;
return len;
}
int
TLSSocket_write(TLSSocket self, uint8_t* buf, int size)
{
int ret;
int len = size;
int len = 0;
checkForCRLUpdate(self);
@ -965,22 +977,26 @@ TLSSocket_write(TLSSocket self, uint8_t* buf, int size)
return -1;
}
while ((ret = mbedtls_ssl_write(&(self->ssl), buf, len)) <= 0)
while (len < size)
{
if (ret == MBEDTLS_ERR_NET_CONN_RESET)
{
DEBUG_PRINT("TLS", "peer closed the connection\n");
return -1;
int ret = mbedtls_ssl_write(&(self->ssl), (buf + len), (size -len));
if ((ret == MBEDTLS_ERR_SSL_WANT_READ) || (ret == MBEDTLS_ERR_SSL_WANT_WRITE) ||
(ret == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS) || (ret == MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS)) {
continue;
}
if ((ret != MBEDTLS_ERR_SSL_WANT_READ) && (ret != MBEDTLS_ERR_SSL_WANT_WRITE))
{
DEBUG_PRINT("TLS", "mbedtls_ssl_write returned %d\n", ret);
if (ret < 0) {
DEBUG_PRINT("TLS", "mbedtls_ssl_write returned -0x%X\n", -ret);
if (0 != (ret = mbedtls_ssl_session_reset(&(self->ssl)))) {
DEBUG_PRINT("TLS", "mbedtls_ssl_session_reset failed -0x%X\n", -ret);
}
return -1;
}
}
len = ret;
len += ret;
}
return len;
}
@ -1008,6 +1024,7 @@ TLSSocket_close(TLSSocket self)
if (self->peerCert)
GLOBAL_FREEMEM(self->peerCert);
GLOBAL_FREEMEM(self);
}

@ -1,18 +1,22 @@
# The SWIG functions/macros used in this module, swig_add_module and swig_add_library
# are not available in CMake versions earlier than 3.8
# cmake_minimum_required(VERSION 3.8)
cmake_minimum_required(VERSION 3.12)
cmake_policy(SET CMP0078 NEW)
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.14.0")
cmake_policy(SET CMP0086 NEW)
endif()
find_package(SWIG REQUIRED)
include(${SWIG_USE_FILE})
find_package(PythonInterp ${BUILD_PYTHON_VERSION} REQUIRED)
find_package(PythonLibs ${PYTHON_VERSION_STRING} EXACT REQUIRED)
find_package(Python COMPONENTS Interpreter Development REQUIRED)
include_directories(${PYTHON_INCLUDE_PATH})
include_directories(${Python_INCLUDE_DIRS})
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
set(CMAKE_SWIG_FLAGS "")
set_property(SOURCE iec61850.i PROPERTY CPLUSPLUS ON)
set_property(SOURCE iec61850.i PROPERTY SWIG_MODULE_NAME pyiec61850)
if(WIN32)
set(LIBS iec61850 ws2_32)
@ -20,25 +24,21 @@ else()
set(LIBS iec61850-shared)
endif()
if(${CMAKE_VERSION} VERSION_LESS 3.8)
swig_add_module(iec61850 python iec61850.i)
else()
swig_add_library(iec61850
LANGUAGE python
SOURCES iec61850.i
)
endif()
swig_add_library(pyiec61850
LANGUAGE python
SOURCES iec61850.i
)
swig_link_libraries(iec61850 ${PYTHON_LIBRARIES} ${LIBS})
swig_link_libraries(pyiec61850 ${LIBS})
# Finding python modules install path
execute_process(
COMMAND ${PYTHON_EXECUTABLE} -c
"from distutils.sysconfig import get_python_lib; import sys; sys.stdout.write(get_python_lib())"
COMMAND ${Python_EXECUTABLE} -c
"from sysconfig import get_path; import sys; sys.stdout.write(get_path('platlib'))"
OUTPUT_VARIABLE PYTHON_SITE_DIR
)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/iec61850.py DESTINATION ${PYTHON_SITE_DIR})
install(TARGETS _iec61850 LIBRARY DESTINATION ${PYTHON_SITE_DIR})
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pyiec61850.py DESTINATION ${PYTHON_SITE_DIR})
install(TARGETS pyiec61850 LIBRARY DESTINATION ${PYTHON_SITE_DIR})
add_test(test_pyiec61850 ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/test_pyiec61850.py)
add_test(test_pyiec61850 ${Python_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/test_pyiec61850.py)

@ -0,0 +1,95 @@
# Python wrapper for libIEC61850
## Building
Before building you should install SWIG and Python
(see the '[Setup development environment on Linux](#setup-development-environment-on-linux-ubuntu)' section for help).
To build the Python bindings you have to turn on the BUILD\_PYTHON\_BINDINGS flag in CMake from cmake-gui or in command line:
```sh
$ mkdir build && cd build
$ cmake -DBUILD_PYTHON_BINDINGS=ON ..
```
Then compile the library and install it:
```sh
$ make
$ sudo make install
```
(Eventually, update your ld cache with: `sudo ldconfig`)
CMake and SWIG will automatically detect your Python version and install the Python library in Python library directories.
For running the integrated tests:
```sh
$ make test
```
pyiec61850 library is to be imported calling:
```python
import pyiec61850 as iec61850
```
## Client tutorial
The Python bindings works similarly to the basic C library. However there are some differences:
* a specific function is to be called to cast variables from one type to another
* arguments passed by pointer are to be removed from arguments and append to the return list
For example to create a connection, call:
```python
con = iec61850.IedConnection_create()
error = iec61850.IedConnection_connect(con, "localhost", 102)
if (error == iec61850.IED_ERROR_OK):
# Do some work
iec61850.IedConnection_close(con)
iec61850.IedConnection_destroy(con)
```
To iterate over a list of logical devices, the code becomes:
```python
[deviceList, error] = iec61850.IedConnection_getLogicalDeviceList(con)
device = iec61850.LinkedList_getNext(deviceList)
while device:
print("LD: %s" % iec61850.toCharP(device.data))
[logicalNodes, error] = iec61850.IedConnection_getLogicalDeviceDirectory(
con, iec61850.toCharP(device.data))
device = iec61850.LinkedList_getNext(device)
iec61850.LinkedList_destroy(deviceList)
```
Reading and writing operations can be performed using this syntax:
```python
[floatValue, error] = iec61850.IedConnection_readFloatValue(con,
"simpleIOGenericIO/GGIO1.AnIn1.mag.f", iec61850.IEC61850_FC_MX)
err = iec61850.IedConnection_writeFloatValue(con,
"simpleIOGenericIO/GGIO1.AnIn1.mag.f", iec61850.IEC61850_FC_MX, 10.0)
```
## Appendix
## Setup development environment on Linux Ubuntu
_[Tested on Ubuntu 20.04 LTS, Ubuntu 22.04 LTS, Ubuntu 23.10]_
Here the minimum required packages for compiling libiec61850 and the Python
wrapper (without TLS, SQlite, ...):
```sh
$ sudo apt-get update
$ sudo apt-get install g++ cmake swig git python3 python3-all-dev
```
## Setup development environment on Linux Alpine
_[Tested on Alpine 3.18]_
Here the minimum required packages for compiling libiec61850 and the Python
wrapper (without TLS, SQlite, ...):
```sh
$ apk update
$ apk add git g++ swig make cmake python3 python3-dev linux-headers
```

@ -1,6 +1,6 @@
#!/usr/bin/python
import os,sys
import iec61850
import pyiec61850 as iec61850
if __name__=="__main__":
hostname = "localhost";
tcpPort = 102

@ -27,7 +27,7 @@ The user needs to:
import time
import sys
import iec61850
import pyiec61850 as iec61850
def open_connection(ip_address, mms_port):

@ -1,5 +1,5 @@
/* File : iec61850.i */
%module(directors="1") iec61850
%module(directors="1") pyiec61850
%ignore ControlObjectClient_setTestMode(ControlObjectClient self);
%ignore CDA_OperBoolean(ModelNode* parent, bool isTImeActivated);
%ignore LogicalNode_hasBufferedReports(LogicalNode* node);

@ -6,7 +6,7 @@ import traceback
import signal
import sys
sys.path.append('.')
import iec61850
import pyiec61850 as iec61850
def signal_handler(signal, frame):
global running
running =0

@ -1,50 +0,0 @@
# Building
Before building you should install swig and python.
To build python bindings you have to turn on the BUILD\_PYTHON\_BINDINGS flag in CMake from cmake-gui or in command line:
```sh
$ cmake -DBUILD_PYTHON_BINDINGS=ON .
```
Then compile the library and install it. CMake and swig will automatically detect your python version and install the python library in python library directories.
pyiec61850 library is to be imported calling
```python
import iec61850
```
# Client tutorial
The python bindings works similarly to the basic C library. However there are some differences:
* a specific function is to be called to cast variables from one type to another
* arguments passed by pointer are to be removed from arguments and append to the return list
For example to create a connection, call:
```python
con = iec61850.IedConnection_create()
error = iec61850.IedConnection_connect(con, "localhost", 102)
if (error == iec61850.IED_ERROR_OK):
# Do some work
iec61850.IedConnection_close(con)
iec61850.IedConnection_destroy(con)
```
To iterate over a list of logical devices, the code becomes:
```python
[deviceList, error] = iec61850.IedConnection_getLogicalDeviceList(con)
device = iec61850.LinkedList_getNext(deviceList)
while device:
print("LD: %s" % iec61850.toCharP(device.data))
[logicalNodes, error] = iec61850.IedConnection_getLogicalDeviceDirectory(
con, iec61850.toCharP(device.data))
device = iec61850.LinkedList_getNext(device)
iec61850.LinkedList_destroy(deviceList)
```
Reading and writing operations can be performed using this syntax:
```python
[floatValue, error] = iec61850.IedConnection_readFloatValue(con,
"simpleIOGenericIO/GGIO1.AnIn1.mag.f", iec61850.IEC61850_FC_MX)
err = iec61850.IedConnection_writeFloatValue(con,
"simpleIOGenericIO/GGIO1.AnIn1.mag.f", iec61850.IEC61850_FC_MX, 10.0)
```

@ -90,14 +90,14 @@ StringUtils_copyStringToBufferAndReplace(const char* str, char* buffer, char old
char*
StringUtils_createStringFromBuffer(const uint8_t* buf, int size)
{
char* newStr = (char*) GLOBAL_MALLOC(size + 1);
char* newStr = (char*) GLOBAL_MALLOC(size + 1);
if (newStr) {
memcpy(newStr, buf, size);
newStr[size] = 0;
}
if (newStr) {
memcpy(newStr, buf, size);
newStr[size] = 0;
}
return newStr;
return newStr;
}
char*
@ -429,11 +429,11 @@ getCharWeight(int c)
{
static bool initialized = false;
static char lookupTable[LT_MAX_CHARS + 1];
static const char* charOrder = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz$_0123456789";
if (!initialized) {
int ltIndex;
int weight = 1;
const char* charOrder = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz$_0123456789";
for (ltIndex = 1; ltIndex < LT_MAX_CHARS; ltIndex++) {
if (strchr(charOrder, ltIndex)) continue;

@ -1,7 +1,7 @@
/*
* goose_publisher.c
*
* Copyright 2013-2022 Michael Zillgith
* Copyright 2013-2024 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -106,12 +106,14 @@ GoosePublisher_createEx(CommParameters* parameters, const char* interfaceID, boo
if (self)
{
if (prepareGooseBuffer(self, parameters, interfaceID, useVlanTag)) {
if (prepareGooseBuffer(self, parameters, interfaceID, useVlanTag))
{
self->timestamp = MmsValue_newUtcTimeByMsTime(Hal_getTimeInMs());
GoosePublisher_reset(self);
}
else {
else
{
GoosePublisher_destroy(self);
self = NULL;
}
@ -257,71 +259,84 @@ prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char*
uint16_t vlanId;
uint16_t appId;
if (parameters) {
dstAddr = defaultDstAddr;
priority = CONFIG_GOOSE_DEFAULT_PRIORITY;
vlanId = CONFIG_GOOSE_DEFAULT_VLAN_ID;
appId = CONFIG_GOOSE_DEFAULT_APPID;
}
else {
if (parameters)
{
dstAddr = parameters->dstAddress;
priority = parameters->vlanPriority;
vlanId = parameters->vlanId;
appId = parameters->appId;
}
else
{
dstAddr = defaultDstAddr;
priority = CONFIG_GOOSE_DEFAULT_PRIORITY;
vlanId = CONFIG_GOOSE_DEFAULT_VLAN_ID;
appId = CONFIG_GOOSE_DEFAULT_APPID;
}
if (interfaceID)
self->ethernetSocket = Ethernet_createSocket(interfaceID, dstAddr);
else
self->ethernetSocket = Ethernet_createSocket(CONFIG_ETHERNET_INTERFACE_ID, dstAddr);
if (self->ethernetSocket) {
if (self->ethernetSocket)
{
self->buffer = (uint8_t*) GLOBAL_MALLOC(GOOSE_MAX_MESSAGE_SIZE);
memcpy(self->buffer, dstAddr, 6);
memcpy(self->buffer + 6, srcAddr, 6);
if (self->buffer)
{
memcpy(self->buffer, dstAddr, 6);
memcpy(self->buffer + 6, srcAddr, 6);
int bufPos = 12;
int bufPos = 12;
if (useVlanTags) {
/* Priority tag - IEEE 802.1Q */
self->buffer[bufPos++] = 0x81;
self->buffer[bufPos++] = 0x00;
if (useVlanTags)
{
/* Priority tag - IEEE 802.1Q */
self->buffer[bufPos++] = 0x81;
self->buffer[bufPos++] = 0x00;
uint8_t tci1 = priority << 5;
tci1 += vlanId / 256;
uint8_t tci1 = priority << 5;
tci1 += vlanId / 256;
uint8_t tci2 = vlanId % 256;
uint8_t tci2 = vlanId % 256;
self->buffer[bufPos++] = tci1; /* Priority + VLAN-ID */
self->buffer[bufPos++] = tci2; /* VLAN-ID */
}
self->buffer[bufPos++] = tci1; /* Priority + VLAN-ID */
self->buffer[bufPos++] = tci2; /* VLAN-ID */
}
/* EtherType GOOSE */
self->buffer[bufPos++] = 0x88;
self->buffer[bufPos++] = 0xB8;
/* EtherType GOOSE */
self->buffer[bufPos++] = 0x88;
self->buffer[bufPos++] = 0xB8;
/* APPID */
self->buffer[bufPos++] = appId / 256;
self->buffer[bufPos++] = appId % 256;
/* APPID */
self->buffer[bufPos++] = appId / 256;
self->buffer[bufPos++] = appId % 256;
self->lengthField = bufPos;
self->lengthField = bufPos;
/* Length */
self->buffer[bufPos++] = 0x00;
self->buffer[bufPos++] = 0x08;
/* Length */
self->buffer[bufPos++] = 0x00;
self->buffer[bufPos++] = 0x08;
/* Reserved1 */
self->buffer[bufPos++] = 0x00;
self->buffer[bufPos++] = 0x00;
/* Reserved1 */
self->buffer[bufPos++] = 0x00;
self->buffer[bufPos++] = 0x00;
/* Reserved2 */
self->buffer[bufPos++] = 0x00;
self->buffer[bufPos++] = 0x00;
/* Reserved2 */
self->buffer[bufPos++] = 0x00;
self->buffer[bufPos++] = 0x00;
self->payloadStart = bufPos;
self->payloadStart = bufPos;
return true;
return true;
}
else
{
if (DEBUG_GOOSE_PUBLISHER)
printf("GOOSE_PUBLISHER: Failed to allocate buffer\n");
return false;
}
}
else {
return false;
@ -329,8 +344,8 @@ prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char*
}
static int32_t
createGoosePayload(GoosePublisher self, LinkedList dataSetValues, uint8_t* buffer, size_t maxPayloadSize) {
createGoosePayload(GoosePublisher self, LinkedList dataSetValues, uint8_t* buffer, size_t maxPayloadSize)
{
/* Step 1 - calculate length fields */
uint32_t goosePduLength = 0;
@ -365,13 +380,16 @@ createGoosePayload(GoosePublisher self, LinkedList dataSetValues, uint8_t* buffe
LinkedList element = LinkedList_getNext(dataSetValues);
while (element) {
while (element)
{
MmsValue* dataSetEntry = (MmsValue*) element->data;
if (dataSetEntry) {
if (dataSetEntry)
{
dataSetSize += MmsValue_encodeMmsData(dataSetEntry, NULL, 0, false);
}
else {
else
{
/* TODO encode MMS NULL */
if (DEBUG_GOOSE_PUBLISHER)
printf("GOOSE_PUBLISHER: NULL value in data set!\n");
@ -438,7 +456,8 @@ createGoosePayload(GoosePublisher self, LinkedList dataSetValues, uint8_t* buffe
/* Encode data set entries */
element = LinkedList_getNext(dataSetValues);
while (element) {
while (element)
{
MmsValue* dataSetEntry = (MmsValue*) element->data;
if (dataSetEntry) {
@ -503,7 +522,7 @@ GoosePublisher_publishAndDump(GoosePublisher self, LinkedList dataSet, char *msg
int rc = GoosePublisher_publish(self, dataSet);
if (rc == 0)
{
{
int copied = self->payloadStart + self->payloadLength;
if (bufSize < copied)

@ -1,7 +1,7 @@
/*
* goose_receiver.c
*
* Copyright 2014-2022 Michael Zillgith
* Copyright 2014-2024 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -73,7 +73,8 @@ GooseReceiver_createEx(uint8_t* buffer)
{
GooseReceiver self = (GooseReceiver) GLOBAL_MALLOC(sizeof(struct sGooseReceiver));
if (self != NULL) {
if (self != NULL)
{
self->running = false;
self->stop = false;
self->interfaceId = NULL;
@ -148,9 +149,20 @@ static void
createNewStringFromBufferElement(MmsValue* value, uint8_t* bufferSrc, int elementLength)
{
value->value.visibleString.buf = (char*) GLOBAL_MALLOC(elementLength + 1);
memcpy(value->value.visibleString.buf, bufferSrc, elementLength);
value->value.visibleString.buf[elementLength] = 0;
value->value.visibleString.size = elementLength;
if (value->value.visibleString.buf)
{
memcpy(value->value.visibleString.buf, bufferSrc, elementLength);
value->value.visibleString.buf[elementLength] = 0;
value->value.visibleString.size = elementLength;
}
else
{
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: failed to allocate memory for visible string\n");
value->value.visibleString.size = 0;
}
}
static GooseParseError
@ -165,24 +177,28 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
GooseParseError pe = GOOSE_PARSE_ERROR_NO_ERROR;
uint8_t tag;
while (bufPos < allDataLength) {
while (bufPos < allDataLength)
{
tag = buffer[bufPos++];
if (elementIndex > maxIndex) {
if (elementIndex > maxIndex)
{
pe = GOOSE_PARSE_ERROR_OVERFLOW;
break; /* from while */
}
MmsValue* value = MmsValue_getElement(dataSetValues, elementIndex);
if (value == NULL) {
if (value == NULL)
{
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: type mismatch (element %i not found)\n", elementIndex);
return GOOSE_PARSE_ERROR_TYPE_MISMATCH;
}
bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, allDataLength);
if (bufPos < 0) {
if (bufPos < 0)
{
pe = GOOSE_PARSE_ERROR_TAGDECODE;
break; /* from while */
}
@ -197,7 +213,8 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
case 0xa1: /* array */
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: found array\n");
if (MmsValue_getType(value) == MMS_ARRAY) {
if (MmsValue_getType(value) == MMS_ARRAY)
{
if (parseAllData(buffer + bufPos, elementLength, value) != GOOSE_PARSE_ERROR_NO_ERROR)
pe = GOOSE_PARSE_ERROR_SUBLEVEL;
}
@ -209,7 +226,8 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
case 0xa2: /* structure */
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: found structure\n");
if (MmsValue_getType(value) == MMS_STRUCTURE) {
if (MmsValue_getType(value) == MMS_STRUCTURE)
{
if (parseAllData(buffer + bufPos, elementLength, value) != GOOSE_PARSE_ERROR_NO_ERROR)
pe = GOOSE_PARSE_ERROR_SUBLEVEL;
}
@ -232,18 +250,23 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
break;
case 0x84: /* BIT STRING */
if (MmsValue_getType(value) == MMS_BIT_STRING) {
if (MmsValue_getType(value) == MMS_BIT_STRING)
{
int padding = buffer[bufPos];
if (padding > 7) {
if (padding > 7)
{
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: invalid bit-string (padding not plausible)\n");
pe = GOOSE_PARSE_ERROR_INVALID_PADDING;
}
else {
else
{
int bitStringLength = (8 * (elementLength - 1)) - padding;
if (bitStringLength == value->value.bitString.size) {
if (bitStringLength == value->value.bitString.size)
{
memcpy(value->value.bitString.buf, buffer + bufPos + 1,
elementLength - 1);
}
@ -258,8 +281,10 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
break;
case 0x85: /* integer */
if (MmsValue_getType(value) == MMS_INTEGER) {
if (elementLength <= value->value.integer->maxSize) {
if (MmsValue_getType(value) == MMS_INTEGER)
{
if (elementLength <= value->value.integer->maxSize)
{
value->value.integer->size = elementLength;
memcpy(value->value.integer->octets, buffer + bufPos, elementLength);
}
@ -273,8 +298,10 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
break;
case 0x86: /* unsigned integer */
if (MmsValue_getType(value) == MMS_UNSIGNED) {
if (elementLength <= value->value.integer->maxSize) {
if (MmsValue_getType(value) == MMS_UNSIGNED)
{
if (elementLength <= value->value.integer->maxSize)
{
value->value.integer->size = elementLength;
memcpy(value->value.integer->octets, buffer + bufPos, elementLength);
}
@ -288,7 +315,8 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
break;
case 0x87: /* Float */
if (MmsValue_getType(value) == MMS_FLOAT) {
if (MmsValue_getType(value) == MMS_FLOAT)
{
if (elementLength == 9) {
MmsValue_setDouble(value, BerDecoder_decodeDouble(buffer, bufPos));
}
@ -305,15 +333,19 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
break;
case 0x89: /* octet string */
if (MmsValue_getType(value) == MMS_OCTET_STRING) {
if (elementLength <= abs(value->value.octetString.maxSize)) {
if (MmsValue_getType(value) == MMS_OCTET_STRING)
{
if (elementLength <= abs(value->value.octetString.maxSize))
{
value->value.octetString.size = elementLength;
memcpy(value->value.octetString.buf, buffer + bufPos, elementLength);
}
else {
else
{
uint8_t* newBuf = (uint8_t*)GLOBAL_MALLOC(elementLength);
if (newBuf) {
if (newBuf)
{
memcpy(newBuf, buffer + bufPos, elementLength);
uint8_t* oldBuf = value->value.octetString.buf;
@ -324,7 +356,6 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
GLOBAL_FREEMEM(oldBuf);
}
}
}
else {
@ -333,14 +364,17 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
break;
case 0x8a: /* visible string */
if (MmsValue_getType(value) == MMS_VISIBLE_STRING) {
if (value->value.visibleString.buf != NULL) {
if ((int32_t) value->value.visibleString.size >= elementLength) {
if (MmsValue_getType(value) == MMS_VISIBLE_STRING)
{
if (value->value.visibleString.buf != NULL)
{
if ((int32_t) value->value.visibleString.size >= elementLength)
{
memcpy(value->value.visibleString.buf, buffer + bufPos, elementLength);
value->value.visibleString.buf[elementLength] = 0;
}
else {
else
{
GLOBAL_FREEMEM(value->value.visibleString.buf);
createNewStringFromBufferElement(value, buffer + bufPos, elementLength);
@ -348,7 +382,6 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
}
else
createNewStringFromBufferElement(value, buffer + bufPos, elementLength);
}
else {
pe = GOOSE_PARSE_ERROR_TYPE_MISMATCH;
@ -356,7 +389,8 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
break;
case 0x8c: /* binary time */
if (MmsValue_getType(value) == MMS_BINARY_TIME) {
if (MmsValue_getType(value) == MMS_BINARY_TIME)
{
if ((elementLength == 4) || (elementLength == 6)) {
memcpy(value->value.binaryTime.buf, buffer + bufPos, elementLength);
}
@ -367,7 +401,8 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
break;
case 0x91: /* Utctime */
if (elementLength == 8) {
if (elementLength == 8)
{
if (MmsValue_getType(value) == MMS_UTC_TIME) {
MmsValue_setUtcTimeByBuffer(value, buffer + bufPos);
}
@ -394,13 +429,15 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
elementIndex++;
}
if (elementIndex <= maxIndex) {
if (elementIndex <= maxIndex)
{
if (pe == GOOSE_PARSE_ERROR_NO_ERROR) {
pe = GOOSE_PARSE_ERROR_UNDERFLOW;
}
}
if (DEBUG_GOOSE_SUBSCRIBER) {
if (DEBUG_GOOSE_SUBSCRIBER)
{
switch (pe) {
case GOOSE_PARSE_ERROR_UNKNOWN_TAG:
printf("GOOSE_SUBSCRIBER: Found unkown tag %02x!\n", tag);
@ -443,11 +480,14 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt
MmsValue* dataSetValues = NULL;
while (bufPos < allDataLength) {
while (bufPos < allDataLength)
{
uint8_t tag = buffer[bufPos++];
bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, allDataLength);
if (bufPos < 0) {
if (bufPos < 0)
{
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n");
return 0;
@ -498,11 +538,14 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt
elementIndex = 0;
bufPos = 0;
while (bufPos < allDataLength) {
while (bufPos < allDataLength)
{
uint8_t tag = buffer[bufPos++];
bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, allDataLength);
if (bufPos < 0) {
if (bufPos < 0)
{
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n");
return 0;
@ -552,22 +595,26 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt
case 0x84: /* BIT STRING */
{
if (elementLength > 1) {
if (elementLength > 1)
{
int padding = buffer[bufPos];
int rawBitLength = (elementLength - 1) * 8;
if (padding > 7) {
if (padding > 7)
{
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: invalid bit-string (padding not plausible)\n");
goto exit_with_error;
}
else {
else
{
value = MmsValue_newBitString(rawBitLength - padding);
memcpy(value->value.bitString.buf, buffer + bufPos + 1, elementLength - 1);
}
}
else {
else
{
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: invalid bit-string\n");
@ -577,13 +624,15 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt
break;
case 0x85: /* integer */
if (elementLength > 8) {
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: unsupported integer size(%i)\n", elementLength);
if (elementLength > 8)
{
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: unsupported integer size(%i)\n", elementLength);
goto exit_with_error;
goto exit_with_error;
}
else {
else
{
value = MmsValue_newInteger(elementLength * 8);
memcpy(value->value.integer->octets, buffer + bufPos, elementLength);
value->value.integer->size = elementLength;
@ -592,13 +641,15 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt
break;
case 0x86: /* unsigned integer */
if (elementLength > 8) {
if (elementLength > 8)
{
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: unsupported unsigned size(%i)\n", elementLength);
goto exit_with_error;
}
else {
else
{
value = MmsValue_newUnsigned(elementLength * 8);
memcpy(value->value.integer->octets, buffer + bufPos, elementLength);
value->value.integer->size = elementLength;
@ -634,13 +685,16 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt
break;
case 0x91: /* Utctime */
if (elementLength == 8) {
if (elementLength == 8)
{
value = MmsValue_newUtcTime(0);
MmsValue_setUtcTimeByBuffer(value, buffer + bufPos);
}
else
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: UTCTime element is of wrong size!\n");
{
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: UTCTime element is of wrong size!\n");
}
break;
default:
@ -651,7 +705,8 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt
bufPos += elementLength;
if (value != NULL) {
if (value != NULL)
{
MmsValue_setElement(dataSetValues, elementIndex, value);
elementIndex++;
}
@ -686,10 +741,12 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength)
uint32_t numberOfDatSetEntries = 0;
if (buffer[bufPos++] == 0x61) {
if (buffer[bufPos++] == 0x61)
{
int gooseLength;
bufPos = BerDecoder_decodeLength(buffer, &gooseLength, bufPos, apduLength);
if (bufPos < 0) {
if (bufPos < 0)
{
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n");
return 0;
@ -697,12 +754,14 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength)
int gooseEnd = bufPos + gooseLength;
while (bufPos < gooseEnd) {
while (bufPos < gooseEnd)
{
int elementLength;
uint8_t tag = buffer[bufPos++];
bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, apduLength);
if (bufPos < 0) {
if (bufPos < 0)
{
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n");
return 0;
@ -720,16 +779,19 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength)
{
LinkedList element = LinkedList_getNext(self->subscriberList);
while (element != NULL) {
while (element)
{
GooseSubscriber subscriber = (GooseSubscriber) LinkedList_getData(element);
if (subscriber->isObserver)
{
if (elementLength > 129) {
if (elementLength > 129)
{
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: gocbRef too long!\n");
}
else {
else
{
memcpy(subscriber->goCBRef, buffer + bufPos, elementLength);
subscriber->goCBRef[elementLength] = 0;
}
@ -737,8 +799,10 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength)
matchingSubscriber = subscriber;
break;
}
else if (subscriber->goCBRefLen == elementLength) {
if (memcmp(subscriber->goCBRef, buffer + bufPos, elementLength) == 0) {
else if (subscriber->goCBRefLen == elementLength)
{
if (memcmp(subscriber->goCBRef, buffer + bufPos, elementLength) == 0)
{
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: gocbRef is matching!\n");
matchingSubscriber = subscriber;
@ -768,12 +832,15 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength)
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Found dataSet\n");
{
if (matchingSubscriber) {
if (elementLength > 129) {
if (matchingSubscriber)
{
if (elementLength > 129)
{
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: datSet too long!\n");
}
else {
else
{
memcpy(matchingSubscriber->datSet, buffer + bufPos, elementLength);
matchingSubscriber->datSet[elementLength] = 0;
}
@ -785,12 +852,15 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength)
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Found goId\n");
{
if (matchingSubscriber) {
if (elementLength > 129) {
if (matchingSubscriber)
{
if (elementLength > 129)
{
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: goId too long!\n");
}
else {
else
{
memcpy(matchingSubscriber->goId, buffer + bufPos, elementLength);
matchingSubscriber->goId[elementLength] = 0;
}
@ -856,15 +926,17 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength)
bufPos += elementLength;
}
if (matchingSubscriber != NULL) {
if (matchingSubscriber != NULL)
{
matchingSubscriber->timeAllowedToLive = timeAllowedToLive;
matchingSubscriber->ndsCom = ndsCom;
matchingSubscriber->simulation = simulation;
if (matchingSubscriber->dataSetValuesSelfAllocated) {
if (matchingSubscriber->dataSetValuesSelfAllocated)
{
/* when confRev changed replaced old data set */
if ((matchingSubscriber->dataSetValues != NULL) && (matchingSubscriber->confRev != confRev)) {
if ((matchingSubscriber->dataSetValues != NULL) && (matchingSubscriber->confRev != confRev))
{
MmsValue_delete(matchingSubscriber->dataSetValues);
matchingSubscriber->dataSetValues = NULL;
}
@ -874,14 +946,16 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength)
if (timestampBufPos)
MmsValue_setUtcTimeByBuffer(matchingSubscriber->timestamp, timestampBufPos);
else {
else
{
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: GOOSE message has no time stamp\n");
MmsValue_setUtcTime(matchingSubscriber->timestamp, 0);
}
if (matchingSubscriber->isObserver && matchingSubscriber->dataSetValues != NULL) {
if (matchingSubscriber->isObserver && matchingSubscriber->dataSetValues != NULL)
{
MmsValue_delete(matchingSubscriber->dataSetValues);
matchingSubscriber->dataSetValues = NULL;
}
@ -890,7 +964,8 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength)
if (matchingSubscriber->dataSetValues == NULL)
matchingSubscriber->dataSetValues = parseAllDataUnknownValue(matchingSubscriber, dataSetBufferAddress, dataSetBufferLength, false);
else {
else
{
GooseParseError parseError = parseAllData(dataSetBufferAddress, dataSetBufferLength, matchingSubscriber->dataSetValues);
if (parseError != GOOSE_PARSE_ERROR_NO_ERROR) {
@ -900,7 +975,8 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength)
matchingSubscriber->parseError = parseError;
}
if (matchingSubscriber->stNum == stNum) {
if (matchingSubscriber->stNum == stNum)
{
if (matchingSubscriber->sqNum >= sqNum) {
isValid = false;
}
@ -944,13 +1020,18 @@ parseGooseMessage(GooseReceiver self, uint8_t* buffer, int numbytes)
uint8_t priority = 0;
uint16_t vlanId = 0;
bool vlanSet = false;
/* check for VLAN tag */
if ((buffer[bufPos] == 0x81) && (buffer[bufPos + 1] == 0x00)) {
if ((buffer[bufPos] == 0x81) && (buffer[bufPos + 1] == 0x00))
{
priority = buffer[bufPos + 2] & 0xF8 >> 5;
vlanId = ((buffer[bufPos + 2] & 0x07) << 8) + buffer[bufPos + 3];
vlanSet = true;
bufPos += 4; /* skip VLAN tag */
headerLength += 4;
if (numbytes < (22 + 4))
return;
}
/* check for GOOSE Ethertype */
@ -980,13 +1061,22 @@ parseGooseMessage(GooseReceiver self, uint8_t* buffer, int numbytes)
int apduLength = length - 8;
if (numbytes < length + headerLength) {
if (apduLength < 0)
{
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Invalid length field\n");
return;
}
if (numbytes < length + headerLength)
{
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: Invalid PDU size\n");
return;
}
if (DEBUG_GOOSE_SUBSCRIBER) {
if (DEBUG_GOOSE_SUBSCRIBER)
{
printf("GOOSE_SUBSCRIBER: GOOSE message:\nGOOSE_SUBSCRIBER: ----------------\n");
printf("GOOSE_SUBSCRIBER: DST-MAC: %02x:%02x:%02x:%02x:%02X:%02X\n",
dstMac[0], dstMac[1], dstMac[2], dstMac[3], dstMac[4], dstMac[5]);
@ -998,7 +1088,8 @@ parseGooseMessage(GooseReceiver self, uint8_t* buffer, int numbytes)
/* check if there is an interested subscriber */
LinkedList element = LinkedList_getNext(self->subscriberList);
while (element != NULL) {
while (element)
{
GooseSubscriber subscriber = (GooseSubscriber) LinkedList_getData(element);
if (subscriber->isObserver)
@ -1014,7 +1105,8 @@ parseGooseMessage(GooseReceiver self, uint8_t* buffer, int numbytes)
}
if (((subscriber->appId == -1) || (subscriber->appId == appId)) &&
(!subscriber->dstMacSet || (memcmp(subscriber->dstMac, dstMac,6) == 0))) {
(!subscriber->dstMacSet || (memcmp(subscriber->dstMac, dstMac,6) == 0)))
{
subscriberFound = true;
break;
}
@ -1036,41 +1128,48 @@ gooseReceiverLoop(void *threadParameter)
{
GooseReceiver self = (GooseReceiver) threadParameter;
if (self->ethSocket) {
if (self->ethSocket)
{
EthernetHandleSet handleSet = EthernetHandleSet_new();
EthernetHandleSet_addSocket(handleSet, self->ethSocket);
if (self->running) {
while (self->running) {
switch (EthernetHandleSet_waitReady(handleSet, 100))
{
case -1:
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: EhtnernetHandleSet_waitReady() failure\n");
break;
case 0:
break;
default:
GooseReceiver_tick(self);
}
bool running = true;
if (self->stop)
break;
while (running)
{
switch (EthernetHandleSet_waitReady(handleSet, 100))
{
case -1:
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: EhtnernetHandleSet_waitReady() failure\n");
break;
case 0:
break;
default:
GooseReceiver_tick(self);
}
GooseReceiver_stopThreadless(self);
if (self->stop)
break;
running = self->running;
}
GooseReceiver_stopThreadless(self);
EthernetHandleSet_destroy(handleSet);
}
#if (CONFIG_IEC61850_R_GOOSE == 1)
else if (self->session) {
#if (CONFIG_IEC61850_R_GOOSE == 0)
else if (self->session)
{
HandleSet handleSet = Handleset_new();
Handleset_addSocket(handleSet, RSession_getSocket(self->session));
while (self->running) {
bool running = true;
while (running)
{
switch (Handleset_waitReady(handleSet, 100))
{
case -1:
@ -1085,6 +1184,8 @@ gooseReceiverLoop(void *threadParameter)
if (self->stop)
break;
running = self->running;
}
GooseReceiver_stopThreadless(self);
@ -1102,7 +1203,8 @@ void
GooseReceiver_start(GooseReceiver self)
{
#if (CONFIG_MMS_THREADLESS_STACK == 0)
if (GooseReceiver_startThreadless(self)) {
if (GooseReceiver_startThreadless(self))
{
self->thread = Thread_create((ThreadExecutionFunction) gooseReceiverLoop, (void*) self, false);
if (self->thread != NULL) {
@ -1160,7 +1262,8 @@ GooseReceiver_stop(GooseReceiver self)
void
GooseReceiver_destroy(GooseReceiver self)
{
if (self) {
if (self)
{
#if (CONFIG_MMS_THREADLESS_STACK == 0)
if ((self->thread != NULL) && (GooseReceiver_isRunning(self)))
GooseReceiver_stop(self);
@ -1185,12 +1288,14 @@ GooseReceiver_startThreadless(GooseReceiver self)
{
#if (CONFIG_IEC61850_R_GOOSE == 1)
if (self->session) {
if (RSession_start(self->session) == R_SESSION_ERROR_OK) {
if (RSession_start(self->session) == R_SESSION_ERROR_OK)
{
self->running = true;
return (EthernetSocket)1;
}
else {
else
{
self->running = false;
return (EthernetSocket)0;
@ -1204,7 +1309,8 @@ GooseReceiver_startThreadless(GooseReceiver self)
else
self->ethSocket = Ethernet_createSocket(self->interfaceId, NULL);
if (self->ethSocket != NULL) {
if (self->ethSocket != NULL)
{
Ethernet_setProtocolFilter(self->ethSocket, ETH_P_GOOSE);
/* set multicast addresses for subscribers */
@ -1212,14 +1318,17 @@ GooseReceiver_startThreadless(GooseReceiver self)
LinkedList element = LinkedList_getNext(self->subscriberList);
while (element != NULL) {
while (element != NULL)
{
GooseSubscriber subscriber = (GooseSubscriber) LinkedList_getData(element);
if (subscriber->dstMacSet == false) {
if (subscriber->dstMacSet == false)
{
/* no destination MAC address defined -> we have to switch to all multicast mode */
Ethernet_setMode(self->ethSocket, ETHERNET_SOCKET_MODE_ALL_MULTICAST);
}
else {
else
{
Ethernet_addMulticastAddress(self->ethSocket, subscriber->dstMac);
}
@ -1262,18 +1371,20 @@ bool
GooseReceiver_tick(GooseReceiver self)
{
#if (CONFIG_IEC61850_R_GOOSE == 1)
if (self->session) {
if (self->session)
{
if (RSession_receiveMessage(self->session, handleSessionPayloadElement, (void*) self) == R_SESSION_ERROR_OK)
return true;
else
return false;
}
else {
else
{
#endif /* (CONFIG_IEC61850_R_GOOSE == 1) */
int packetSize = Ethernet_receivePacket(self->ethSocket, self->buffer, ETH_BUFFER_LENGTH);
if (packetSize > 0) {
if (packetSize > 0)
{
parseGooseMessage(self, self->buffer, packetSize);
return true;
}

@ -1,7 +1,7 @@
/*
* goose_subscriber.c
*
* Copyright 2013-2022 Michael Zillgith
* Copyright 2013-2024 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -40,7 +40,8 @@ GooseSubscriber_create(char* goCbRef, MmsValue* dataSetValues)
{
GooseSubscriber self = (GooseSubscriber) GLOBAL_CALLOC(1, sizeof(struct sGooseSubscriber));
if (self) {
if (self)
{
StringUtils_copyStringMax(self->goCBRef, 130, goCbRef);
self->goCBRefLen = strlen(goCbRef);
@ -97,7 +98,8 @@ GooseSubscriber_setAppId(GooseSubscriber self, uint16_t appId)
void
GooseSubscriber_destroy(GooseSubscriber self)
{
if (self) {
if (self)
{
MmsValue_delete(self->timestamp);
if (self->dataSetValuesSelfAllocated)
@ -120,19 +122,19 @@ GooseSubscriber_getAppId(GooseSubscriber self)
return self->appId;
}
char *
char*
GooseSubscriber_getGoId(GooseSubscriber self)
{
return self->goId;
}
char *
char*
GooseSubscriber_getGoCbRef(GooseSubscriber self)
{
return self->goCBRef;
}
char *
char*
GooseSubscriber_getDataSet(GooseSubscriber self)
{
return self->datSet;

@ -3,7 +3,7 @@
*
* Client implementation for IEC 61850 reporting.
*
* Copyright 2013-2022 Michael Zillgith
* Copyright 2013-2024 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -283,14 +283,27 @@ lookupReportHandler(IedConnection self, const char* rcbReference)
return NULL;
}
static void
uninstallReportHandler(IedConnection self, const char* rcbReference)
{
ClientReport report = lookupReportHandler(self, rcbReference);
if (report != NULL) {
LinkedList_remove(self->enabledReports, report);
ClientReport_destroy(report);
}
}
void
IedConnection_installReportHandler(IedConnection self, const char* rcbReference, const char* rptId, ReportCallbackFunction handler,
void* handlerParameter)
{
Semaphore_wait(self->reportHandlerMutex);
ClientReport report = lookupReportHandler(self, rcbReference);
if (report != NULL) {
IedConnection_uninstallReportHandler(self, rcbReference);
uninstallReportHandler(self, rcbReference);
if (DEBUG_IED_CLIENT)
printf("DEBUG_IED_CLIENT: Removed existing report callback handler for %s\n", rcbReference);
@ -306,8 +319,8 @@ IedConnection_installReportHandler(IedConnection self, const char* rcbReference,
else
report->rptId = NULL;
Semaphore_wait(self->reportHandlerMutex);
LinkedList_add(self->enabledReports, report);
Semaphore_post(self->reportHandlerMutex);
if (DEBUG_IED_CLIENT)
@ -319,12 +332,7 @@ IedConnection_uninstallReportHandler(IedConnection self, const char* rcbReferenc
{
Semaphore_wait(self->reportHandlerMutex);
ClientReport report = lookupReportHandler(self, rcbReference);
if (report != NULL) {
LinkedList_remove(self->enabledReports, report);
ClientReport_destroy(report);
}
uninstallReportHandler(self, rcbReference);
Semaphore_post(self->reportHandlerMutex);
}
@ -367,6 +375,8 @@ IedConnection_triggerGIReport(IedConnection self, IedClientError* error, const c
void
iedConnection_handleReport(IedConnection self, MmsValue* value)
{
Semaphore_wait(self->reportHandlerMutex);
MmsValue* rptIdValue = MmsValue_getElement(value, 0);
if ((rptIdValue == NULL) || (MmsValue_getType(rptIdValue) != MMS_VISIBLE_STRING)) {
@ -769,15 +779,14 @@ iedConnection_handleReport(IedConnection self, MmsValue* value)
matchingReport->reasonForInclusion[i] = IEC61850_REASON_NOT_INCLUDED;
}
}
Semaphore_wait(self->reportHandlerMutex);
if (matchingReport->callback != NULL)
matchingReport->callback(matchingReport->callbackParameter, matchingReport);
exit_function:
Semaphore_post(self->reportHandlerMutex);
exit_function:
return;
}

@ -528,39 +528,44 @@ readObjectHandlerInternal(uint32_t invokeId, void* parameter, MmsError err, MmsV
IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId);
if (call) {
if (call)
{
IedConnection_GetRCBValuesHandler handler = (IedConnection_GetRCBValuesHandler) call->callback;
ClientReportControlBlock updateRcb = (ClientReportControlBlock) call->specificParameter;
char* rcbReference = (char*) call->specificParameter2.pointer;
if (err != MMS_ERROR_NONE) {
if (err != MMS_ERROR_NONE)
{
handler(invokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(err), NULL);
}
else {
if (value == NULL) {
else
{
if (value == NULL)
{
handler(invokeId, call->callbackParameter, IED_ERROR_OBJECT_DOES_NOT_EXIST, NULL);
}
else {
if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) {
else
{
if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR)
{
if (DEBUG_IED_CLIENT)
printf("DEBUG_IED_CLIENT: getRCBValues returned data-access-error!\n");
handler(invokeId, call->callbackParameter, iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value)), NULL);
}
else {
else
{
ClientReportControlBlock returnRcb = updateRcb;
if (returnRcb == NULL)
returnRcb = ClientReportControlBlock_create(rcbReference);
if (clientReportControlBlock_updateValues(returnRcb, value)) {
if (clientReportControlBlock_updateValues(returnRcb, value))
{
handler(invokeId, call->callbackParameter, IED_ERROR_OK, returnRcb);
}
else {
else
{
if (DEBUG_IED_CLIENT)
printf("DEBUG_IED_CLIENT: getRCBValues returned wrong type!\n");
@ -569,19 +574,18 @@ readObjectHandlerInternal(uint32_t invokeId, void* parameter, MmsError err, MmsV
if (updateRcb == NULL)
ClientReportControlBlock_destroy(returnRcb);
}
}
MmsValue_delete(value);
}
}
GLOBAL_FREEMEM(rcbReference);
iedConnection_releaseOutstandingCall(self, call);
}
else {
else
{
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: internal error - no matching outstanding call!\n");
}
@ -598,7 +602,8 @@ IedConnection_getRCBValuesAsync(IedConnection self, IedClientError* error, const
char* domainName = MmsMapping_getMmsDomainFromObjectReference(rcbReference, domainId);
if (domainName == NULL) {
if (domainName == NULL)
{
*error = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT;
return 0;
}
@ -608,7 +613,8 @@ IedConnection_getRCBValuesAsync(IedConnection self, IedClientError* error, const
IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self);
if (call == NULL) {
if (call == NULL)
{
*error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED;
return 0;
}
@ -627,7 +633,8 @@ IedConnection_getRCBValuesAsync(IedConnection self, IedClientError* error, const
*error = iedConnection_mapMmsErrorToIedError(err);
if (err != MMS_ERROR_NONE) {
if (err != MMS_ERROR_NONE)
{
GLOBAL_FREEMEM(call->specificParameter2.pointer);
iedConnection_releaseOutstandingCall(self, call);
return 0;

File diff suppressed because it is too large Load Diff

@ -1,7 +1,7 @@
/*
* iec61850_client.h
*
* Copyright 2013-2021 Michael Zillgith
* Copyright 2013-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -172,6 +172,14 @@ typedef enum {
IED_ERROR_UNKNOWN = 99
} IedClientError;
/**
* \brief Convert error value to string
*
* \return string constant representing the error
*/
LIB61850_API const char*
IedClientError_toString(IedClientError err);
/**************************************************
* Connection creation and destruction
**************************************************/
@ -258,6 +266,16 @@ IedConnection_setLocalAddress(IedConnection self, const char* localIpAddress, in
LIB61850_API void
IedConnection_setConnectTimeout(IedConnection self, uint32_t timeoutInMs);
/**
* \brief Set the maximum number outstanding calls allowed for this connection
*
* \param self the connection object
* \param calling the maximum outstanding calls allowed by the caller (client)
* \param called the maximum outstanding calls allowed by the called endpoint (server)
*/
LIB61850_API void
IedConnection_setMaxOutstandingCalls(IedConnection self, int calling, int called);
/**
* \brief set the request timeout in ms
*
@ -1154,15 +1172,6 @@ typedef int ReasonForInclusion;
/** the reason for inclusion is unknown (e.g. report is not configured to include reason-for-inclusion) */
#define IEC61850_REASON_UNKNOWN 32
#define REASON_NOT_INCLUDED IEC61850_REASON_NOT_INCLUDED
#define REASON_DATA_CHANGE IEC61850_REASON_DATA_CHANGE
#define REASON_QUALITY_CHANGE IEC61850_REASON_QUALITY_CHANGE
#define REASON_DATA_UPDATE IEC61850_REASON_DATA_UPDATE
#define REASON_INTEGRITY IEC61850_REASON_INTEGRITY
#define REASON_GI IEC61850_REASON_GI
#define REASON_UNKNOWN IEC61850_REASON_UNKNOWN
/* Element encoding mask values for ClientReportControlBlock */
/** include the report ID into the setRCB request */
@ -1259,9 +1268,11 @@ typedef void (*ReportCallbackFunction) (void* parameter, ClientReport report);
* Otherwise the internal data structures storing the received data set values will not be updated
* correctly.
*
* When replacing a report handler you only have to call this function. There is no separate call to
* \note Replacing a report handler you only have to call this function. There is no separate call to
* IedConnection_uninstallReportHandler() required.
*
* \note Do not call this function inside of the ReportCallbackFunction. Doing so will cause a deadlock.
*
* \param self the connection object
* \param rcbReference object reference of the report control block
* \param rptId a string that identifies the report. If the rptId is not available then the
@ -1276,6 +1287,8 @@ IedConnection_installReportHandler(IedConnection self, const char* rcbReference,
/**
* \brief uninstall a report handler function for the specified report control block (RCB)
*
* \note Do not call this function inside of the ReportCallbackFunction. Doing so will cause a deadlock.
*
* \param self the connection object
* \param rcbReference object reference of the report control block
*/

@ -3,7 +3,7 @@
*
* IEC 61850 server API for libiec61850.
*
* Copyright 2013-2023 Michael Zillgith
* Copyright 2013-2024 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -815,7 +815,9 @@ LIB61850_API void
IedServer_setConnectionIndicationHandler(IedServer self, IedConnectionIndicationHandler handler, void* parameter);
/**
* \brief Ignore all requests from clients
* \brief Ignore all requests from clients (for testing purposes)
*
* NOTE: This function will block all client requests on MMS layer
*
* \param self the instance of IedServer to configure.
* \param enable when true all requests from clients will be ignored
@ -1447,7 +1449,7 @@ LIB61850_API DataObject*
ControlAction_getControlObject(ControlAction self);
/**
* \brief Gets the time of the control, if it's a timeActivatedControl, returns 0, if it's not.
* \brief Gets the time of the control (attribute "operTm"), if it's a timeActivatedControl, returns 0, if it's not.
*
* \param self the control action instance
*
@ -1456,6 +1458,16 @@ ControlAction_getControlObject(ControlAction self);
LIB61850_API uint64_t
ControlAction_getControlTime(ControlAction self);
/**
* \brief Gets the time (attribute "T") of the last received control action (Oper or Select)
*
* \param self the control action instance
*
* \return the time of the last received control action
*/
LIB61850_API Timestamp*
ControlAction_getT(ControlAction self);
/**
* \brief Control model callback to perform the static tests (optional).
*
@ -1845,6 +1857,19 @@ LIB61850_API void
IedServer_handleWriteAccessForComplexAttribute(IedServer self, DataAttribute* dataAttribute,
WriteAccessHandler handler, void* parameter);
/**
* \brief Install a WriteAccessHandler for all data attributes of a data object with a specific FC
*
* \param self the instance of IedServer to operate on.
* \param dataObject the data object to monitor
* \param fc the functional constraint to monitor
* \param handler the callback function that is invoked if a client tries to write to
* the monitored data attribute.
* \param parameter a user provided parameter that is passed to the WriteAccessHandler when called.
*/
LIB61850_API void
IedServer_handleWriteAccessForDataObject(IedServer self, DataObject* dataObject, FunctionalConstraint fc, WriteAccessHandler handler, void* parameter);
typedef enum {
ACCESS_POLICY_ALLOW,
ACCESS_POLICY_DENY
@ -1997,6 +2022,15 @@ typedef bool
LIB61850_API void
IedServer_setControlBlockAccessHandler(IedServer self, IedServer_ControlBlockAccessHandler handler, void* parameter);
/**
* \brief Temporarily ignore read requests (for testing purposes)
*
* \param self the instance of IedServer to operate on.
* \param ignore true to ignore read requests, false to handle read requests.
*/
LIB61850_API void
IedServer_ignoreReadAccess(IedServer self, bool ignore);
/**@}*/
/**@}*/

@ -1,7 +1,7 @@
/*
* control.h
*
* Copyright 2013-2019 Michael Zillgith
* Copyright 2013-2024 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -90,6 +90,8 @@ struct sControlObject
MmsValue* origin;
MmsValue* timestamp;
Timestamp T;
MmsValue* ctlNumSt;
MmsValue* originSt;

@ -1,7 +1,7 @@
/*
* ied_connection_private.h
*
* Copyright 2013-2022 Michael Zillgith
* Copyright 2013-2024 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -34,14 +34,16 @@
typedef struct sIedConnectionOutstandingCall* IedConnectionOutstandingCall;
struct sIedConnectionOutstandingCall {
struct sIedConnectionOutstandingCall
{
bool used;
uint32_t invokeId;
void* callback;
void* callbackParameter;
void* specificParameter; /* function/service specific parameter */
union {
union
{
void* pointer;
struct {
uint32_t originalInvokeId;
@ -69,6 +71,7 @@ struct sIedConnection
Semaphore outstandingCallsLock;
IedConnectionOutstandingCall outstandingCalls;
int maxOutstandingCalled;
IedConnectionClosedHandler connectionCloseHandler;
void* connectionClosedParameter;
@ -81,7 +84,8 @@ struct sIedConnection
uint8_t timeQuality;
};
struct sClientReportControlBlock {
struct sClientReportControlBlock
{
char* objectReference;
bool isBuffered;

@ -81,10 +81,11 @@ struct sIedServer
uint8_t timeQuality; /* user settable time quality for internally updated times */
bool ignoreReadAccess; /* when true don't answer read request (for test purposes) */
bool running;
};
LIB61850_INTERNAL IEC61850_ServiceError
private_IedServer_convertMmsDataAccessErrorToServiceError(MmsDataAccessError mmsError);

@ -124,7 +124,7 @@ LIB61850_INTERNAL MmsValue*
LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, MmsServerConnection connection);
LIB61850_INTERNAL MmsDataAccessError
LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig,
LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* domain, const char* variableIdOrig,
MmsValue* value, MmsServerConnection connection);
#endif /* LIBIEC61850_SRC_IEC61850_INC_PRIVATE_LOGGING_H_ */

@ -149,7 +149,7 @@ LIB61850_INTERNAL void
MmsMapping_installReadAccessHandler(MmsMapping* self, ReadAccessHandler handler, void* paramter);
LIB61850_INTERNAL MmsDataAccessError
Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* variableIdOrig,
Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, const char* variableIdOrig,
MmsValue* value, MmsServerConnection connection);
LIB61850_INTERNAL MmsValue*

@ -41,7 +41,7 @@ LIB61850_INTERNAL MmsValue*
LIBIEC61850_SV_readAccessSampledValueControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig , MmsServerConnection connection);
LIB61850_INTERNAL MmsDataAccessError
LIBIEC61850_SV_writeAccessSVControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig,
LIBIEC61850_SV_writeAccessSVControlBlock(MmsMapping* self, MmsDomain* domain, const char* variableIdOrig,
MmsValue* value, MmsServerConnection connection);
LIB61850_INTERNAL void

@ -126,7 +126,7 @@ LIB61850_INTERNAL void
ReportControl_valueUpdated(ReportControl* self, int dataSetEntryIndex, int flag, bool modelLocked);
LIB61850_INTERNAL MmsValue*
ReportControl_getRCBValue(ReportControl* rc, char* elementName);
ReportControl_getRCBValue(ReportControl* rc, const char* elementName);
LIB61850_INTERNAL MmsVariableSpecification*
Reporting_createMmsBufferedRCBs(MmsMapping* self, MmsDomain* domain,
@ -137,7 +137,7 @@ Reporting_createMmsUnbufferedRCBs(MmsMapping* self, MmsDomain* domain,
LogicalNode* logicalNode, int reportsCount);
LIB61850_INTERNAL MmsDataAccessError
Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* elementName, MmsValue* value,
Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, const char* elementName, MmsValue* value,
MmsServerConnection connection);
LIB61850_INTERNAL bool

@ -1,7 +1,7 @@
/*
* client_connection.c
*
* Copyright 2013-2022 Michael Zillgith
* Copyright 2013-2024 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -32,8 +32,8 @@
#include "libiec61850_platform_includes.h"
struct sClientConnection {
struct sClientConnection
{
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore tasksCountMutex;
#endif
@ -47,7 +47,8 @@ private_ClientConnection_create(void* serverConnectionHandle)
{
ClientConnection self = (ClientConnection) GLOBAL_MALLOC(sizeof(struct sClientConnection));
if (self) {
if (self)
{
#if (CONFIG_MMS_THREADLESS_STACK != 1)
self->tasksCountMutex = Semaphore_create(1);
#endif
@ -62,7 +63,8 @@ private_ClientConnection_create(void* serverConnectionHandle)
void
private_ClientConnection_destroy(ClientConnection self)
{
if (self) {
if (self)
{
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_destroy(self->tasksCountMutex);
#endif
@ -123,7 +125,6 @@ private_ClientConnection_getServerConnectionHandle(ClientConnection self)
return self->serverConnectionHandle;
}
const char*
ClientConnection_getPeerAddress(ClientConnection self)
{

@ -540,17 +540,18 @@ updateDataSetsWithCachedValues(IedServer self)
}
}
IedServer
IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguration, IedServerConfig serverConfiguration)
{
IedServer self = (IedServer) GLOBAL_CALLOC(1, sizeof(struct sIedServer));
if (self) {
if (self)
{
self->model = dataModel;
self->running = false;
self->localIpAddress = NULL;
self->ignoreReadAccess = false;
#if (CONFIG_IEC61850_EDITION_1 == 1)
self->edition = IEC_61850_EDITION_1;
@ -561,7 +562,8 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio
#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1)
self->logServiceEnabled = true;
if (serverConfiguration) {
if (serverConfiguration)
{
self->logServiceEnabled = serverConfiguration->enableLogService;
self->edition = serverConfiguration->edition;
}
@ -574,7 +576,8 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio
#endif /* (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) */
#if (CONFIG_IEC61850_REPORT_SERVICE == 1)
if (serverConfiguration) {
if (serverConfiguration)
{
self->reportBufferSizeBRCBs = serverConfiguration->reportBufferSize;
self->reportBufferSizeURCBs = serverConfiguration->reportBufferSizeURCBs;
self->enableBRCBResvTms = serverConfiguration->enableResvTmsForBRCB;
@ -582,7 +585,8 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio
self->syncIntegrityReportTimes = serverConfiguration->syncIntegrityReportTimes;
self->rcbSettingsWritable = serverConfiguration->reportSettingsWritable;
}
else {
else
{
self->reportBufferSizeBRCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE;
self->reportBufferSizeURCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE;
self->enableOwnerForRCB = false;
@ -602,11 +606,13 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio
#endif
#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
if (serverConfiguration) {
if (serverConfiguration)
{
self->enableEditSG = serverConfiguration->enableEditSG;
self->hasSGCBResvTms = serverConfiguration->enableResvTmsForSGCB;
}
else {
else
{
self->enableEditSG = true;
self->hasSGCBResvTms = true;
}
@ -614,14 +620,15 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio
self->mmsMapping = MmsMapping_create(dataModel, self);
if (self->mmsMapping) {
if (self->mmsMapping)
{
self->mmsDevice = MmsMapping_getMmsDeviceModel(self->mmsMapping);
self->mmsServer = MmsServer_create(self->mmsDevice, tlsConfiguration);
#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1)
if (serverConfiguration) {
if (serverConfiguration)
{
MmsServer_enableFileService(self->mmsServer, serverConfiguration->enableFileService);
MmsServer_enableDynamicNamedVariableListService(self->mmsServer, serverConfiguration->enableDynamicDataSetService);
MmsServer_setMaxAssociationSpecificDataSets(self->mmsServer, serverConfiguration->maxAssociationSpecificDataSets);
@ -668,7 +675,8 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio
IedServer_setTimeQuality(self, true, false, false, 10);
}
else {
else
{
IedServer_destroy(self);
self = NULL;
}
@ -1679,6 +1687,38 @@ IedServer_handleWriteAccessForComplexAttribute(IedServer self, DataAttribute* da
}
}
void
IedServer_handleWriteAccessForDataObject(IedServer self, DataObject* dataObject, FunctionalConstraint fc, WriteAccessHandler handler, void* parameter)
{
if (dataObject == NULL) {
if (DEBUG_IED_SERVER)
printf("IED_SERVER: IedServer_handlerWriteAccessForDataObject - dataObject == NULL!\n");
}
else
{
ModelNode* childElement = dataObject->firstChild;
while (childElement)
{
if (childElement->modelType == DataAttributeModelType)
{
DataAttribute* dataAttribute = (DataAttribute*) childElement;
if (dataAttribute->fc == fc)
{
IedServer_handleWriteAccessForComplexAttribute(self, dataAttribute, handler, parameter);
}
}
else if (childElement->modelType == DataObjectModelType)
{
IedServer_handleWriteAccessForDataObject(self, (DataObject*) childElement, fc, handler, parameter);
}
childElement = childElement->sibling;
}
}
}
void
IedServer_setReadAccessHandler(IedServer self, ReadAccessHandler handler, void* parameter)
{
@ -1960,3 +2000,9 @@ IedServer_setControlBlockAccessHandler(IedServer self, IedServer_ControlBlockAcc
self->mmsMapping->controlBlockAccessHandler = handler;
self->mmsMapping->controlBlockAccessHandlerParameter = parameter;
}
void
IedServer_ignoreReadAccess(IedServer self, bool ignore)
{
self->ignoreReadAccess = ignore;
}

File diff suppressed because it is too large Load Diff

@ -482,7 +482,7 @@ copyLCBValuesToTrackingObject(MmsMapping* self, LogControl* logControl)
#endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */
MmsDataAccessError
LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig,
LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* domain, const char* variableIdOrig,
MmsValue* value, MmsServerConnection connection)
{
(void)connection;

@ -1,7 +1,7 @@
/*
* mms_mapping.c
*
* Copyright 2013-2023 Michael Zillgith
* Copyright 2013-2024 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -1335,8 +1335,8 @@ checkForServiceTrackingVariables(MmsMapping* self, LogicalNode* logicalNode)
{
ModelNode* modelNode = logicalNode->firstChild;
while (modelNode) {
while (modelNode)
{
if (!strcmp(modelNode->name, "SpcTrk") || !strcmp(modelNode->name, "DpcTrk") ||
!strcmp(modelNode->name, "IncTrk") || !strcmp(modelNode->name, "EncTrk1") ||
!strcmp(modelNode->name, "ApcFTrk") || !strcmp(modelNode->name, "ApcIntTrk") ||
@ -2344,7 +2344,7 @@ lookupGCB(MmsMapping* self, MmsDomain* domain, char* lnName, char* objectName)
#endif
static MmsDataAccessError
writeAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig,
writeAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, const char* variableIdOrig,
MmsValue* value, MmsServerConnection connection)
{
char variableId[130];
@ -2499,43 +2499,6 @@ writeAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variable
#endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */
#if 0
static MmsValue*
checkIfValueBelongsToModelNode(DataAttribute* dataAttribute, MmsValue* value, MmsValue* newValue)
{
if (dataAttribute->mmsValue == value)
return newValue;
DataAttribute* child = (DataAttribute*) dataAttribute->firstChild;
while (child != NULL) {
MmsValue* tmpValue = checkIfValueBelongsToModelNode(child, value, newValue);
if (tmpValue != NULL)
return tmpValue;
else
child = (DataAttribute*) child->sibling;
}
if (MmsValue_getType(value) == MMS_STRUCTURE) {
int elementCount = MmsValue_getArraySize(value);
int i;
for (i = 0; i < elementCount; i++) {
MmsValue* childValue = MmsValue_getElement(value, i);
MmsValue* childNewValue = MmsValue_getElement(newValue, i);
MmsValue* tmpValue = checkIfValueBelongsToModelNode(dataAttribute, childValue, childNewValue);
if (tmpValue != NULL)
return tmpValue;
}
}
return NULL;
}
#endif
static FunctionalConstraint
getFunctionalConstraintForWritableNode(char* separator)
{
@ -2605,16 +2568,28 @@ getAccessPolicyForFC(MmsMapping* self, FunctionalConstraint fc)
static MmsDataAccessError
mmsWriteHandler(void* parameter, MmsDomain* domain,
char* variableId, MmsValue* value, MmsServerConnection connection)
const char* variableId, int arrayIdx, const char* componentId, MmsValue* value, MmsServerConnection connection)
{
MmsMapping* self = (MmsMapping*) parameter;
if (DEBUG_IED_SERVER)
printf("IED_SERVER: Write requested %s\n", variableId);
{
if (arrayIdx != -1) {
if (componentId) {
printf("IED_SERVER: Write requested %s(%i).%s\n", variableId, arrayIdx, componentId);
}
else {
printf("IED_SERVER: Write requested %s(%i)\n", variableId, arrayIdx);
}
}
else {
printf("IED_SERVER: Write requested %s\n", variableId);
}
}
/* Access control based on functional constraint */
char* separator = strchr(variableId, '$');
char* separator = (char*)strchr(variableId, '$');
if (separator == NULL)
return DATA_ACCESS_ERROR_INVALID_ADDRESS;
@ -2691,7 +2666,7 @@ mmsWriteHandler(void* parameter, MmsDomain* domain,
if (rcNameLen == variableIdLen) {
if (strncmp(variableId, rc->name, variableIdLen) == 0) {
char* elementName = variableId + rcNameLen + 1;
const char* elementName = variableId + rcNameLen + 1;
return Reporting_RCBWriteAccessHandler(self, rc, elementName, value, connection);
}
@ -2713,6 +2688,10 @@ mmsWriteHandler(void* parameter, MmsDomain* domain,
if (nextSep != NULL) {
nextSep = strchr(nextSep + 1, '$');
if (nextSep == NULL) {
return DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED;
}
char* nameId = nextSep + 1;
/* check access permissions */
@ -2942,13 +2921,19 @@ mmsWriteHandler(void* parameter, MmsDomain* domain,
#endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */
/* writable data model elements - SP, SV, CF, DC, BL */
if (fc != IEC61850_FC_NONE) {
if (fc != IEC61850_FC_NONE)
{
MmsValue* cachedValue;
cachedValue = MmsServer_getValueFromCache(self->mmsServer, domain, variableId);
if (cachedValue) {
if (arrayIdx != -1) {
cachedValue = MmsServer_getValueFromCacheEx2(self->mmsServer, domain, variableId, arrayIdx, componentId);
}
else {
cachedValue = MmsServer_getValueFromCache(self->mmsServer, domain, variableId);
}
if (cachedValue)
{
if (!MmsValue_equalTypes(cachedValue, value)) {
return DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID;
}
@ -2961,7 +2946,8 @@ mmsWriteHandler(void* parameter, MmsDomain* domain,
printf("IED_SERVER: write to %s policy:%i\n", variableId, nodeAccessPolicy);
#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
if (isFunctionalConstraint("SE", separator)) {
if (isFunctionalConstraint("SE", separator))
{
SettingGroup* sg = getSettingGroupByMmsDomain(self, domain);
if (sg != NULL) {
@ -2978,12 +2964,13 @@ mmsWriteHandler(void* parameter, MmsDomain* domain,
/* Call write access handlers */
LinkedList writeHandlerListElement = LinkedList_getNext(self->attributeAccessHandlers);
while (writeHandlerListElement != NULL) {
while (writeHandlerListElement)
{
AttributeAccessHandler* accessHandler = (AttributeAccessHandler*) writeHandlerListElement->data;
DataAttribute* dataAttribute = accessHandler->attribute;
if (dataAttribute->mmsValue == cachedValue) {
if (dataAttribute->mmsValue == cachedValue)
{
ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer,
connection);
@ -2991,7 +2978,8 @@ mmsWriteHandler(void* parameter, MmsDomain* domain,
accessHandler->handler(dataAttribute, value, clientConnection,
accessHandler->parameter);
if ((handlerResult == DATA_ACCESS_ERROR_SUCCESS) || (handlerResult == DATA_ACCESS_ERROR_SUCCESS_NO_UPDATE)) {
if ((handlerResult == DATA_ACCESS_ERROR_SUCCESS) || (handlerResult == DATA_ACCESS_ERROR_SUCCESS_NO_UPDATE))
{
handlerFound = true;
if (handlerResult == DATA_ACCESS_ERROR_SUCCESS_NO_UPDATE)
@ -3007,14 +2995,14 @@ mmsWriteHandler(void* parameter, MmsDomain* domain,
}
/* DENY access if no handler is found and default policy is DENY */
if (!handlerFound) {
if (!handlerFound)
{
if (nodeAccessPolicy == ACCESS_POLICY_DENY)
return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
}
if (updateValue) {
if (updateValue)
{
DataAttribute* da = IedModel_lookupDataAttributeByMmsValue(self->model, cachedValue);
if (da)
@ -3161,7 +3149,6 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo
}
#endif
#if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1)
/* GOOSE control blocks - GO */
if (isGooseControlBlock(separator)) {
@ -3188,8 +3175,8 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo
#if (CONFIG_IEC61850_REPORT_SERVICE == 1)
/* Report control blocks - BR, RP */
if (isReportControlBlock(separator)) {
if (isReportControlBlock(separator))
{
LinkedList reportControls = self->reportControls;
LinkedList nextElement = reportControls;
@ -3210,11 +3197,12 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo
else
variableIdLen = strlen(variableId);
while ((nextElement = LinkedList_getNext(nextElement)) != NULL) {
while ((nextElement = LinkedList_getNext(nextElement)) != NULL)
{
ReportControl* rc = (ReportControl*) nextElement->data;
if (rc->domain == domain) {
if (rc->domain == domain)
{
int parentLNNameStrLen = strlen(rc->parentLN->name);
if (parentLNNameStrLen != lnNameLength)
@ -3223,7 +3211,8 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo
if (memcmp(rc->parentLN->name, variableId, parentLNNameStrLen) != 0)
continue;
if (strlen(rc->name) == variableIdLen) {
if (strlen(rc->name) == variableIdLen)
{
if (strncmp(variableId, rc->name, variableIdLen) == 0)
{
char* elementName = MmsMapping_getNextNameElement(reportName);
@ -3640,15 +3629,26 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS
if (DEBUG_IED_SERVER)
printf("IED_SERVER: mmsReadAccessHandler: Requested %s\n", variableId);
if (self->iedServer->ignoreReadAccess)
{
if (DEBUG_IED_SERVER)
printf("IED_SERVER: mmsReadAccessHandler - ignore request\n");
return DATA_ACCESS_ERROR_NO_RESPONSE;
}
char* separator = strchr(variableId, '$');
#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
if (separator) {
if (isFunctionalConstraint("SE", separator)) {
if (separator)
{
if (isFunctionalConstraint("SE", separator))
{
SettingGroup* sg = getSettingGroupByMmsDomain(self, domain);
if (sg != NULL) {
if (sg != NULL)
{
if (sg->sgcb->editSG == 0)
return DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
}
@ -3670,7 +3670,8 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS
{
FunctionalConstraint fc = IEC61850_FC_NONE;
if (separator != NULL) {
if (separator != NULL)
{
fc = FunctionalConstraint_fromString(separator + 1);
if (fc == IEC61850_FC_BR || fc == IEC61850_FC_US ||
@ -3691,23 +3692,27 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS
{
char* doStart = strchr(separator + 1, '$');
if (doStart != NULL) {
if (doStart != NULL)
{
char* doEnd = strchr(doStart + 1, '$');
if (doEnd == NULL) {
if (doEnd == NULL)
{
StringUtils_copyStringToBuffer(doStart + 1, str);
}
else {
else
{
doEnd--;
StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) (doStart + 1), doEnd - doStart);
}
if (fc == IEC61850_FC_SP) {
if (fc == IEC61850_FC_SP)
{
if (!strcmp(str, "SGCB"))
{
if (self->controlBlockAccessHandler) {
if (self->controlBlockAccessHandler)
{
ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer,
connection);
@ -3722,10 +3727,10 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS
ModelNode* dobj = ModelNode_getChild((ModelNode*) ln, str);
if (dobj != NULL) {
if (dobj->modelType == DataObjectModelType) {
if (dobj != NULL)
{
if (dobj->modelType == DataObjectModelType)
{
ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer,
connection);
@ -3734,7 +3739,8 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS
}
}
}
else {
else
{
ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer,
connection);
@ -3744,10 +3750,12 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS
}
}
}
else {
else
{
LogicalNode* ln = LogicalDevice_getLogicalNode(ld, variableId);
if (ln != NULL) {
if (ln != NULL)
{
ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection);
return self->readAccessHandler(ld, ln, NULL, fc, clientConnection,
@ -4219,10 +4227,12 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag)
bool modelLocked = self->isModelLocked;
while ((element = LinkedList_getNext(element)) != NULL) {
while ((element = LinkedList_getNext(element)) != NULL)
{
ReportControl* rc = (ReportControl*) element->data;
if (rc->enabled || (rc->buffered && rc->dataSet != NULL)) {
if (rc->enabled || (rc->buffered && rc->dataSet != NULL))
{
int index;
switch (flag) {

@ -131,7 +131,7 @@ MmsSampledValueControlBlock_isEnabled(MmsSampledValueControlBlock self)
}
MmsDataAccessError
LIBIEC61850_SV_writeAccessSVControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig,
LIBIEC61850_SV_writeAccessSVControlBlock(MmsMapping* self, MmsDomain* domain, const char* variableIdOrig,
MmsValue* value, MmsServerConnection connection)
{
char variableId[130];

@ -1,7 +1,7 @@
/*
* reporting.c
*
* Copyright 2013-2023 Michael Zillgith
* Copyright 2013-2024 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -185,7 +185,8 @@ purgeBuf(ReportControl* rc)
static void
deleteDataSetValuesShadowBuffer(ReportControl* self)
{
if (self->bufferedDataSetValues != NULL) {
if (self->bufferedDataSetValues != NULL)
{
assert(self->dataSet != NULL);
int dataSetSize = DataSet_getSize(self->dataSet);
@ -252,7 +253,7 @@ ReportControl_destroy(ReportControl* self)
}
MmsValue*
ReportControl_getRCBValue(ReportControl* rc, char* elementName)
ReportControl_getRCBValue(ReportControl* rc, const char* elementName)
{
if (rc->buffered) {
if (strcmp(elementName, "RptID") == 0)
@ -460,7 +461,7 @@ copyRCBValuesToTrackingObject(MmsMapping* self, ReportControl* rc)
}
static void
updateSingleTrackingValue(MmsMapping* self, ReportControl* rc, char* name, MmsValue* newValue)
updateSingleTrackingValue(MmsMapping* self, ReportControl* rc, const char* name, MmsValue* newValue)
{
if (rc->buffered) {
if (self->brcbTrk) {
@ -1906,7 +1907,7 @@ reserveRcb(ReportControl* rc, MmsServerConnection connection)
}
MmsDataAccessError
Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* elementName, MmsValue* value,
Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, const char* elementName, MmsValue* value,
MmsServerConnection connection)
{
MmsDataAccessError retVal = DATA_ACCESS_ERROR_SUCCESS;
@ -2911,13 +2912,14 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
int dataBlockSize = 0;
if (isIntegrity || isGI) {
if (isIntegrity || isGI)
{
DataSetEntry* dataSetEntry = reportControl->dataSet->fcdas;
int i;
for (i = 0; i < inclusionBitStringSize; i++) {
for (i = 0; i < inclusionBitStringSize; i++)
{
/* don't need reason for inclusion in GI or integrity report */
int encodedSize;
@ -2925,7 +2927,8 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
if (dataSetEntry->value) {
encodedSize = MmsValue_encodeMmsData(dataSetEntry->value, NULL, 0, false);
}
else {
else
{
MmsValue _errVal;
_errVal.type = MMS_DATA_ACCESS_ERROR;
_errVal.value.dataAccessError = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID;
@ -2940,17 +2943,19 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
bufferEntrySize += MemoryAllocator_getAlignedSize(sizeof(int) + dataBlockSize); /* add aligned_size(LEN + DATA) */
}
else { /* other trigger reason */
else
{
/* other trigger reason */
bufferEntrySize += inclusionFieldSize;
int reasonForInclusionSize = 0;
int i;
for (i = 0; i < inclusionBitStringSize; i++) {
if (reportControl->inclusionFlags[i] != REPORT_CONTROL_NONE) {
for (i = 0; i < inclusionBitStringSize; i++)
{
if (reportControl->inclusionFlags[i] != REPORT_CONTROL_NONE)
{
reasonForInclusionSize++;
assert(reportControl->bufferedDataSetValues[i] != NULL);
@ -2985,13 +2990,15 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
if (DEBUG_IED_SERVER)
printf("IED_SERVER: number of reports in report buffer: %i\n", buffer->reportsCount);
if (buffer->lastEnqueuedReport == NULL) { /* buffer is empty - we start at the beginning of the memory block */
if (buffer->lastEnqueuedReport == NULL)
{
/* buffer is empty - we start at the beginning of the memory block */
entryBufPos = buffer->memoryBlock;
buffer->oldestReport = (ReportBufferEntry*) entryBufPos;
buffer->nextToTransmit = (ReportBufferEntry*) entryBufPos;
}
else {
else
{
assert(buffer->lastEnqueuedReport != NULL);
assert(buffer->oldestReport != NULL);
@ -3003,12 +3010,16 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
if (DEBUG_IED_SERVER) printf ("IED_SERVER: Last buffer offset: %i\n", (int) ((uint8_t*) buffer->lastEnqueuedReport - buffer->memoryBlock));
if (buffer->lastEnqueuedReport == buffer->oldestReport) { /* --> buffer->reportsCount == 1 */
if (buffer->lastEnqueuedReport == buffer->oldestReport)
{
/* --> buffer->reportsCount == 1 */
assert(buffer->reportsCount == 1);
entryBufPos = (uint8_t*) ((uint8_t*) buffer->lastEnqueuedReport + buffer->lastEnqueuedReport->entryLength);
if ((entryBufPos + bufferEntrySize) > (buffer->memoryBlock + buffer->memoryBlockSize)) { /* buffer overflow */
if ((entryBufPos + bufferEntrySize) > (buffer->memoryBlock + buffer->memoryBlockSize))
{
/* buffer overflow */
entryBufPos = buffer->memoryBlock;
#if (DEBUG_IED_SERVER == 1)
@ -3022,7 +3033,8 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
buffer->oldestReport->next = NULL;
buffer->nextToTransmit = NULL;
}
else {
else
{
if (buffer->nextToTransmit == buffer->oldestReport)
buffer->nextToTransmit = buffer->lastEnqueuedReport;
@ -3031,17 +3043,22 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
}
}
else if (buffer->lastEnqueuedReport > buffer->oldestReport) {
else if (buffer->lastEnqueuedReport > buffer->oldestReport)
{
entryBufPos = (uint8_t*) ((uint8_t*) buffer->lastEnqueuedReport + buffer->lastEnqueuedReport->entryLength);
if ((entryBufPos + bufferEntrySize) > (buffer->memoryBlock + buffer->memoryBlockSize)) { /* buffer overflow */
if ((entryBufPos + bufferEntrySize) > (buffer->memoryBlock + buffer->memoryBlockSize))
{
/* buffer overflow */
entryBufPos = buffer->memoryBlock;
/* remove old reports until enough space for new entry is available */
while ((entryBufPos + bufferEntrySize) > (uint8_t*) buffer->oldestReport) {
while ((entryBufPos + bufferEntrySize) > (uint8_t*) buffer->oldestReport)
{
assert(buffer->oldestReport != NULL);
if (buffer->nextToTransmit == buffer->oldestReport) {
if (buffer->nextToTransmit == buffer->oldestReport)
{
buffer->nextToTransmit = buffer->oldestReport->next;
buffer->isOverflow = true;
overflow = true;
@ -3056,7 +3073,8 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
buffer->reportsCount--;
if (buffer->oldestReport == NULL) {
if (buffer->oldestReport == NULL)
{
buffer->oldestReport = (ReportBufferEntry*) entryBufPos;
buffer->oldestReport->next = NULL;
break;
@ -3066,14 +3084,18 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
buffer->lastEnqueuedReport->next = (ReportBufferEntry*) entryBufPos;
}
else if (buffer->lastEnqueuedReport < buffer->oldestReport) {
else if (buffer->lastEnqueuedReport < buffer->oldestReport)
{
entryBufPos = (uint8_t*) ((uint8_t*) buffer->lastEnqueuedReport + buffer->lastEnqueuedReport->entryLength);
if ((entryBufPos + bufferEntrySize) > (buffer->memoryBlock + buffer->memoryBlockSize)) { /* buffer overflow */
if ((entryBufPos + bufferEntrySize) > (buffer->memoryBlock + buffer->memoryBlockSize))
{
/* buffer overflow */
entryBufPos = buffer->memoryBlock;
/* remove older reports in upper buffer part */
while ((uint8_t*) buffer->oldestReport > buffer->memoryBlock) {
while ((uint8_t*) buffer->oldestReport > buffer->memoryBlock)
{
assert(buffer->oldestReport != NULL);
if (buffer->nextToTransmit == buffer->oldestReport) {
@ -3093,13 +3115,15 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
}
/* remove older reports in lower buffer part that will be overwritten by new report */
while ((entryBufPos + bufferEntrySize) > (uint8_t*) buffer->oldestReport) {
while ((entryBufPos + bufferEntrySize) > (uint8_t*) buffer->oldestReport)
{
if (buffer->oldestReport == NULL)
break;
assert(buffer->oldestReport != NULL);
if (buffer->nextToTransmit == buffer->oldestReport) {
if (buffer->nextToTransmit == buffer->oldestReport)
{
buffer->nextToTransmit = buffer->oldestReport->next;
buffer->isOverflow = true;
overflow = true;
@ -3115,15 +3139,17 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
buffer->reportsCount--;
}
}
else {
while (((entryBufPos + bufferEntrySize) > (uint8_t*) buffer->oldestReport) && ((uint8_t*) buffer->oldestReport != buffer->memoryBlock)) {
else
{
while (((entryBufPos + bufferEntrySize) > (uint8_t*) buffer->oldestReport) && ((uint8_t*) buffer->oldestReport != buffer->memoryBlock))
{
if (buffer->oldestReport == NULL)
break;
assert(buffer->oldestReport != NULL);
if (buffer->nextToTransmit == buffer->oldestReport) {
if (buffer->nextToTransmit == buffer->oldestReport)
{
buffer->nextToTransmit = buffer->oldestReport->next;
buffer->isOverflow = true;
overflow = true;
@ -3154,7 +3180,8 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
entry->timeOfEntry = timeOfEntry;
if (isBuffered) {
if (isBuffered)
{
/* ENTRY_ID is set to system time in ms! */
uint64_t entryId = timeOfEntry;
@ -3173,7 +3200,8 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
printf(" at pos %p\n", entryStartPos);
#endif
if (reportControl->enabled == false) {
if (reportControl->enabled == false)
{
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(reportControl->rcbValuesLock);
#endif
@ -3200,7 +3228,8 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
entryBufPos += MemoryAllocator_getAlignedSize(sizeof(ReportBufferEntry));
if (isIntegrity || isGI) {
if (isIntegrity || isGI)
{
DataSetEntry* dataSetEntry = reportControl->dataSet->fcdas;
/* encode LEN */
@ -3210,12 +3239,13 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
/* encode DATA */
int i;
for (i = 0; i < inclusionBitStringSize; i++) {
for (i = 0; i < inclusionBitStringSize; i++)
{
if (dataSetEntry->value) {
entryBufPos += MmsValue_encodeMmsData(dataSetEntry->value, entryBufPos, 0, true);
}
else {
else
{
MmsValue _errVal;
_errVal.type = MMS_DATA_ACCESS_ERROR;
_errVal.value.dataAccessError = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID;
@ -3225,9 +3255,9 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
dataSetEntry = dataSetEntry->sibling;
}
}
else {
else
{
/* encode inclusion bit string */
inclusionFieldStatic.value.bitString.buf = entryBufPos;
memset(entryBufPos, 0, inclusionFieldSize);
@ -3240,10 +3270,10 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
/* encode DATA */
int i;
for (i = 0; i < inclusionBitStringSize; i++) {
if (reportControl->inclusionFlags[i] != REPORT_CONTROL_NONE) {
for (i = 0; i < inclusionBitStringSize; i++)
{
if (reportControl->inclusionFlags[i] != REPORT_CONTROL_NONE)
{
/* update inclusion bit string for report entry */
MmsValue_setBitStringBit(inclusionField, i, true);
@ -3251,13 +3281,14 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
entryBufPos += MmsValue_encodeMmsData(reportControl->bufferedDataSetValues[i], entryBufPos, 0, true);
}
}
/* encode REASON */
for (i = 0; i < inclusionBitStringSize; i++) {
for (i = 0; i < inclusionBitStringSize; i++)
{
if (reportControl->inclusionFlags[i] != REPORT_CONTROL_NONE) {
if (reportControl->inclusionFlags[i] != REPORT_CONTROL_NONE)
{
*entryBufPos = (uint8_t) reportControl->inclusionFlags[i];
entryBufPos ++;
}
@ -3286,10 +3317,12 @@ exit_function:
Semaphore_post(buffer->lock);
#endif
if (reportControl->server) {
if (reportControl->server)
{
MmsMapping* mmsMapping = reportControl->server->mmsMapping;
if (mmsMapping->rcbEventHandler) {
if (mmsMapping->rcbEventHandler)
{
if (overflow) {
mmsMapping->rcbEventHandler(mmsMapping->rcbEventHandlerParameter, reportControl->rcb, NULL, RCB_EVENT_OVERFLOW, NULL, DATA_ACCESS_ERROR_SUCCESS);
}
@ -3980,11 +4013,12 @@ Reporting_activateBufferedReports(MmsMapping* self)
static void
processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs)
{
if ((rc->enabled) || (rc->isBuffering)) {
if (rc->triggerOps & TRG_OPT_GI) {
if (rc->gi) {
if ((rc->enabled) || (rc->isBuffering))
{
if (rc->triggerOps & TRG_OPT_GI)
{
if (rc->gi)
{
/* send current events in event buffer before GI report */
if (rc->triggered) {
rc->triggered = false;
@ -3999,12 +4033,12 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs)
}
}
if (rc->triggerOps & TRG_OPT_INTEGRITY) {
if (rc->intgPd > 0) {
if (currentTimeInMs >= rc->nextIntgReportTime) {
if (rc->triggerOps & TRG_OPT_INTEGRITY)
{
if (rc->intgPd > 0)
{
if (currentTimeInMs >= rc->nextIntgReportTime)
{
/* send current events in event buffer before integrity report */
if (rc->triggered) {
enqueueReport(rc, false, false, currentTimeInMs);
@ -4019,8 +4053,8 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs)
}
/* check for system time change effects */
if ((rc->nextIntgReportTime < currentTimeInMs) || (rc->nextIntgReportTime > currentTimeInMs + rc->intgPd)) {
if ((rc->nextIntgReportTime < currentTimeInMs) || (rc->nextIntgReportTime > currentTimeInMs + rc->intgPd))
{
if (rc->server->syncIntegrityReportTimes) {
rc->nextIntgReportTime = getNextRoundedStartTime(currentTimeInMs, rc->intgPd);
}
@ -4033,9 +4067,11 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs)
rc->triggered = false;
}
else {
else
{
/* check for system time change effects */
if ((rc->nextIntgReportTime < currentTimeInMs) || (rc->nextIntgReportTime > currentTimeInMs + rc->intgPd)) {
if ((rc->nextIntgReportTime < currentTimeInMs) || (rc->nextIntgReportTime > currentTimeInMs + rc->intgPd))
{
if (rc->server->syncIntegrityReportTimes) {
rc->nextIntgReportTime = getNextRoundedStartTime(currentTimeInMs, rc->intgPd);
}
@ -4047,9 +4083,10 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs)
}
}
if (rc->triggered) {
if (currentTimeInMs >= rc->reportTime) {
if (rc->triggered)
{
if (currentTimeInMs >= rc->reportTime)
{
enqueueReport(rc, false, false, currentTimeInMs);
rc->triggered = false;
@ -4065,11 +4102,12 @@ Reporting_processReportEvents(MmsMapping* self, uint64_t currentTimeInMs)
Semaphore_wait(self->isModelLockedMutex);
#endif
if (self->isModelLocked == false) {
if (self->isModelLocked == false)
{
LinkedList element = self->reportControls;
while ((element = LinkedList_getNext(element)) != NULL ) {
while ((element = LinkedList_getNext(element)) != NULL )
{
ReportControl* rc = (ReportControl*) element->data;
ReportControl_lockNotify(rc);
@ -4093,11 +4131,12 @@ Reporting_sendReports(MmsMapping* self, MmsServerConnection connection)
{
LinkedList element = LinkedList_getNext(self->reportControls);
while (element) {
while (element)
{
ReportControl* rc = (ReportControl*) LinkedList_getData(element);
if (rc->clientConnection == connection) {
if (rc->clientConnection == connection)
{
ReportControl_lockNotify(rc);
if (rc->enabled) {
@ -4124,8 +4163,10 @@ static void
copyValuesToReportBuffer(ReportControl* self)
{
int i;
for (i = 0; i < self->dataSet->elementCount; i++) {
if (self->inclusionFlags[i] & REPORT_CONTROL_NOT_UPDATED) {
for (i = 0; i < self->dataSet->elementCount; i++)
{
if (self->inclusionFlags[i] & REPORT_CONTROL_NOT_UPDATED)
{
copySingleValueToReportBuffer(self, i);
/* clear not-updated flag */
@ -4142,13 +4183,14 @@ Reporting_processReportEventsAfterUnlock(MmsMapping* self)
uint64_t currentTime = Hal_getTimeInMs();
while ((element = LinkedList_getNext(element)) != NULL ) {
while ((element = LinkedList_getNext(element)) != NULL )
{
ReportControl* rc = (ReportControl*) element->data;
ReportControl_lockNotify(rc);
if ((rc->enabled) || (rc->isBuffering)) {
if ((rc->enabled) || (rc->isBuffering))
{
if (rc->triggered) {
copyValuesToReportBuffer(rc);
@ -4166,10 +4208,13 @@ ReportControl_valueUpdated(ReportControl* self, int dataSetEntryIndex, int flag,
{
ReportControl_lockNotify(self);
if (self->inclusionFlags[dataSetEntryIndex] & flag) { /* report for this data set entry is already pending (bypass BufTm) */
if (self->inclusionFlags[dataSetEntryIndex] & flag)
{
/* report for this data set entry is already pending (bypass BufTm and send report immediately) */
self->reportTime = Hal_getTimeInMs();
if (modelLocked) {
if (modelLocked)
{
/* buffer all relevant values */
copyValuesToReportBuffer(self);
}
@ -4177,19 +4222,21 @@ ReportControl_valueUpdated(ReportControl* self, int dataSetEntryIndex, int flag,
processEventsForReport(self, self->reportTime);
}
if (modelLocked) {
if (modelLocked)
{
/* set flag to update values when report is to be sent or data model unlocked */
self->inclusionFlags[dataSetEntryIndex] = self->inclusionFlags[dataSetEntryIndex] | flag | REPORT_CONTROL_NOT_UPDATED;
}
else {
else
{
self->inclusionFlags[dataSetEntryIndex] = flag;
/* buffer value for report */
copySingleValueToReportBuffer(self, dataSetEntryIndex);
}
if (self->triggered == false) {
if (self->triggered == false)
{
uint64_t currentTime = Hal_getTimeInMs();
MmsValue_setBinaryTime(self->timeOfEntry, currentTime);
@ -4218,7 +4265,8 @@ ReportControlBlock_getRptEna(ReportControlBlock* self)
char*
ReportControlBlock_getRptID(ReportControlBlock* self)
{
if (self->trgOps & 64) {
if (self->trgOps & 64)
{
ReportControl* rc = (ReportControl*)(self->sibling);
#if (CONFIG_MMS_THREADLESS_STACK != 1)

@ -1,7 +1,7 @@
/*
* config_file_parser.c
*
* Copyright 2014-2022 Michael Zillgith
* Copyright 2014-2024 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -28,9 +28,9 @@
#include "libiec61850_platform_includes.h"
#include "stack_config.h"
#define READ_BUFFER_MAX_SIZE 1024
#include <ctype.h>
static uint8_t lineBuffer[READ_BUFFER_MAX_SIZE];
#define READ_BUFFER_MAX_SIZE 1024
static int
readLine(FileHandle fileHandle, uint8_t* buffer, int maxSize)
@ -114,18 +114,132 @@ ConfigFileParser_createModelFromConfigFileEx(const char* filename)
return model;
}
static bool
setValue(char* lineBuffer, DataAttribute* dataAttribute)
{
char* valueIndicator = strchr((char*) lineBuffer, '=');
if (valueIndicator != NULL) {
switch (dataAttribute->type) {
case IEC61850_UNICODE_STRING_255:
{
char* stringStart = valueIndicator + 2;
terminateString(stringStart, '"');
dataAttribute->mmsValue = MmsValue_newMmsString(stringStart);
}
break;
case IEC61850_VISIBLE_STRING_255:
case IEC61850_VISIBLE_STRING_129:
case IEC61850_VISIBLE_STRING_65:
case IEC61850_VISIBLE_STRING_64:
case IEC61850_VISIBLE_STRING_32:
case IEC61850_CURRENCY:
{
char* stringStart = valueIndicator + 2;
terminateString(stringStart, '"');
dataAttribute->mmsValue = MmsValue_newVisibleString(stringStart);
}
break;
case IEC61850_INT8:
case IEC61850_INT16:
case IEC61850_INT32:
case IEC61850_INT64:
case IEC61850_INT128:
case IEC61850_ENUMERATED:
{
int32_t intValue;
if (sscanf(valueIndicator + 1, "%i", &intValue) != 1) goto exit_error;
dataAttribute->mmsValue = MmsValue_newIntegerFromInt32(intValue);
}
break;
case IEC61850_INT8U:
case IEC61850_INT16U:
case IEC61850_INT24U:
case IEC61850_INT32U:
{
uint32_t uintValue;
if (sscanf(valueIndicator + 1, "%u", &uintValue) != 1) goto exit_error;
dataAttribute->mmsValue = MmsValue_newUnsignedFromUint32(uintValue);
}
break;
case IEC61850_FLOAT32:
{
float floatValue;
if (sscanf(valueIndicator + 1, "%f", &floatValue) != 1) goto exit_error;
dataAttribute->mmsValue = MmsValue_newFloat(floatValue);
}
break;
case IEC61850_FLOAT64:
{
double doubleValue;
if (sscanf(valueIndicator + 1, "%lf", &doubleValue) != 1) goto exit_error;
dataAttribute->mmsValue = MmsValue_newDouble(doubleValue);
}
break;
case IEC61850_BOOLEAN:
{
int boolean;
if (sscanf(valueIndicator + 1, "%i", &boolean) != 1) goto exit_error;
dataAttribute->mmsValue = MmsValue_newBoolean((bool) boolean);
}
break;
case IEC61850_OPTFLDS:
{
int value;
if (sscanf(valueIndicator + 1, "%i", &value) != 1) goto exit_error;
dataAttribute->mmsValue = MmsValue_newBitString(-10);
MmsValue_setBitStringFromIntegerBigEndian(dataAttribute->mmsValue, value);
}
break;
case IEC61850_TRGOPS:
{
int value;
if (sscanf(valueIndicator + 1, "%i", &value) != 1) goto exit_error;
dataAttribute->mmsValue = MmsValue_newBitString(-6);
MmsValue_setBitStringFromIntegerBigEndian(dataAttribute->mmsValue, value);
}
break;
default:
break;
}
}
return true;
exit_error:
return false;
}
IedModel*
ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
{
uint8_t* lineBuffer = (uint8_t*)GLOBAL_MALLOC(READ_BUFFER_MAX_SIZE);
if (lineBuffer == NULL)
goto exit_error;
int bytesRead = 1;
bool stateInModel = false;
int indendation = 0;
bool inArray = false;
bool inArrayElement = false;
IedModel* model = NULL;
LogicalDevice* currentLD = NULL;
LogicalNode* currentLN = NULL;
ModelNode* currentModelNode = NULL;
ModelNode* currentArrayNode = NULL;
DataSet* currentDataSet = NULL;
GSEControlBlock* currentGoCB = NULL;
SVControlBlock* currentSMVCB = NULL;
@ -136,16 +250,30 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
int currentLine = 0;
while (bytesRead > 0) {
while (bytesRead > 0)
{
bytesRead = readLine(fileHandle, lineBuffer, READ_BUFFER_MAX_SIZE);
currentLine++;
if (bytesRead > 0) {
if (bytesRead > 0)
{
lineBuffer[bytesRead] = 0;
if (stateInModel) {
/* trim trailing spaces */
while (bytesRead > 1) {
bytesRead--;
if (isspace(lineBuffer[bytesRead])) {
lineBuffer[bytesRead] = 0;
}
else {
break;
}
}
if (stateInModel)
{
if (StringUtils_startsWith((char*) lineBuffer, "}")) {
if (indendation == 1) {
stateInModel = false;
@ -161,13 +289,25 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
indendation = 3;
}
else if (indendation > 4) {
if (inArrayElement && currentModelNode->parent == currentArrayNode) {
inArrayElement = false;
}
else {
indendation--;
}
if (inArray && currentModelNode == currentArrayNode) {
inArray = false;
}
currentModelNode = currentModelNode->parent;
indendation--;
}
}
else if (indendation == 1) {
if (StringUtils_startsWith((char*) lineBuffer, "LD")) {
else if (indendation == 1)
{
if (StringUtils_startsWith((char*) lineBuffer, "LD"))
{
indendation = 2;
char ldName[65];
@ -178,7 +318,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
terminateString(nameString, ')');
if (ldName[0] != 0) {
if (ldName[0] != 0)
{
terminateString(ldName, ')');
currentLD = LogicalDevice_createEx(nameString, model, ldName);
@ -190,8 +331,10 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
else
goto exit_error;
}
else if (indendation == 2) {
if (StringUtils_startsWith((char*) lineBuffer, "LN")) {
else if (indendation == 2)
{
if (StringUtils_startsWith((char*) lineBuffer, "LN"))
{
indendation = 3;
if (sscanf((char*) lineBuffer, "LN(%129s)", nameString) < 1)
@ -204,26 +347,35 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
else
goto exit_error;
}
else if (indendation == 3) {
if (StringUtils_startsWith((char*) lineBuffer, "DO")) {
else if (indendation == 3)
{
if (StringUtils_startsWith((char*) lineBuffer, "DO"))
{
indendation = 4;
int arrayElements = 0;
sscanf((char*) lineBuffer, "DO(%129s %i)", nameString, &arrayElements);
if (sscanf((char*)lineBuffer, "DO(%129s %i)", nameString, &arrayElements) != 2) {
goto exit_error;
}
currentModelNode = (ModelNode*)
DataObject_create(nameString, (ModelNode*) currentLN, arrayElements);
}
else if (StringUtils_startsWith((char*) lineBuffer, "DS")) {
else if (StringUtils_startsWith((char*) lineBuffer, "DS"))
{
indendation = 4;
sscanf((char*) lineBuffer, "DS(%129s)", nameString);
if (sscanf((char*)lineBuffer, "DS(%129s)", nameString) != 1) {
goto exit_error;
}
terminateString(nameString, ')');
currentDataSet = DataSet_create(nameString, currentLN);
}
else if (StringUtils_startsWith((char*) lineBuffer, "RC")) {
else if (StringUtils_startsWith((char*) lineBuffer, "RC"))
{
int isBuffered;
uint32_t confRef;
int trgOps;
@ -250,7 +402,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
ReportControlBlock_create(nameString, currentLN, rptId,
(bool) isBuffered, dataSetName, confRef, trgOps, options, bufTm, intgPd);
}
else if (StringUtils_startsWith((char*) lineBuffer, "LC")) {
else if (StringUtils_startsWith((char*) lineBuffer, "LC"))
{
uint32_t trgOps;
uint32_t intgPd;
int logEna;
@ -271,7 +424,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
LogControlBlock_create(nameString, currentLN, dataSet, logRef, trgOps, intgPd, logEna, withReasonCode);
}
else if (StringUtils_startsWith((char*) lineBuffer, "LOG")) {
else if (StringUtils_startsWith((char*) lineBuffer, "LOG"))
{
int matchedItems = sscanf((char*) lineBuffer, "LOG(%129s)", nameString);
if (matchedItems < 1) goto exit_error;
@ -281,7 +435,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
Log_create(nameString, currentLN);
}
else if (StringUtils_startsWith((char*) lineBuffer, "GC")) {
else if (StringUtils_startsWith((char*) lineBuffer, "GC"))
{
uint32_t confRef;
int fixedOffs;
int minTime = -1;
@ -296,9 +451,9 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
nameString3, confRef, fixedOffs, minTime, maxTime);
indendation = 4;
}
else if (StringUtils_startsWith((char*) lineBuffer, "SMVC")) {
else if (StringUtils_startsWith((char*) lineBuffer, "SMVC"))
{
uint32_t confRev;
int smpMod;
int smpRate;
@ -313,10 +468,10 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
currentSMVCB = SVControlBlock_create(nameString, currentLN, nameString2, nameString3, confRev, smpMod, smpRate, optFlds, (bool) isUnicast);
indendation = 4;
}
#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
else if (StringUtils_startsWith((char*) lineBuffer, "SG")) {
else if (StringUtils_startsWith((char*) lineBuffer, "SG"))
{
if (strcmp(currentLN->name, "LLN0") != 0) {
if (DEBUG_IED_SERVER)
@ -345,8 +500,10 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
}
}
else if (indendation > 3) {
if (StringUtils_startsWith((char*) lineBuffer, "DO")) {
else if (indendation > 3)
{
if (StringUtils_startsWith((char*) lineBuffer, "DO"))
{
indendation++;
int arrayElements = 0;
@ -356,129 +513,116 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
if (matchedItems != 2) goto exit_error;
currentModelNode = (ModelNode*) DataObject_create(nameString, currentModelNode, arrayElements);
}
else if (StringUtils_startsWith((char*) lineBuffer, "DA")) {
int arrayElements = 0;
if (arrayElements > 0)
{
inArray = true;
currentArrayNode = currentModelNode;
}
}
else if (StringUtils_startsWith((char*) lineBuffer, "["))
{
if (inArray == false) {
goto exit_error;
}
int attributeType = 0;
int functionalConstraint = 0;
int triggerOptions = 0;
uint32_t sAddr = 0;
int arrayIndex;
sscanf((char*) lineBuffer, "DA(%129s %i %i %i %i %u)", nameString, &arrayElements, &attributeType, &functionalConstraint, &triggerOptions, &sAddr);
if (sscanf((char*)lineBuffer, "[%i]", &arrayIndex) != 1) {
goto exit_error;
}
DataAttribute* dataAttribute = DataAttribute_create(nameString, currentModelNode,
(DataAttributeType) attributeType, (FunctionalConstraint) functionalConstraint, triggerOptions, arrayElements, sAddr);
if (arrayIndex < 0) {
goto exit_error;
}
char* valueIndicator = strchr((char*) lineBuffer, '=');
if (currentArrayNode->modelType == DataAttributeModelType)
{
if (StringUtils_endsWith((char*)lineBuffer, ";"))
{
/* array of basic data attribute */
ModelNode* arrayElementNode = ModelNode_getChildWithIdx(currentArrayNode, arrayIndex);
if (valueIndicator != NULL) {
switch (dataAttribute->type) {
case IEC61850_UNICODE_STRING_255:
{
char* stringStart = valueIndicator + 2;
terminateString(stringStart, '"');
dataAttribute->mmsValue = MmsValue_newMmsString(stringStart);
}
break;
case IEC61850_VISIBLE_STRING_255:
case IEC61850_VISIBLE_STRING_129:
case IEC61850_VISIBLE_STRING_65:
case IEC61850_VISIBLE_STRING_64:
case IEC61850_VISIBLE_STRING_32:
case IEC61850_CURRENCY:
{
char* stringStart = valueIndicator + 2;
terminateString(stringStart, '"');
dataAttribute->mmsValue = MmsValue_newVisibleString(stringStart);
}
break;
case IEC61850_INT8:
case IEC61850_INT16:
case IEC61850_INT32:
case IEC61850_INT64:
case IEC61850_INT128:
case IEC61850_ENUMERATED:
{
int32_t intValue;
if (sscanf(valueIndicator + 1, "%i", &intValue) != 1) goto exit_error;
dataAttribute->mmsValue = MmsValue_newIntegerFromInt32(intValue);
if (arrayElementNode) {
setValue((char*)lineBuffer, (DataAttribute*)arrayElementNode);
}
break;
case IEC61850_INT8U:
case IEC61850_INT16U:
case IEC61850_INT24U:
case IEC61850_INT32U:
{
uint32_t uintValue;
if (sscanf(valueIndicator + 1, "%u", &uintValue) != 1) goto exit_error;
dataAttribute->mmsValue = MmsValue_newUnsignedFromUint32(uintValue);
else {
goto exit_error;
}
break;
case IEC61850_FLOAT32:
{
float floatValue;
if (sscanf(valueIndicator + 1, "%f", &floatValue) != 1) goto exit_error;
dataAttribute->mmsValue = MmsValue_newFloat(floatValue);
}
break;
}
else if (StringUtils_endsWith((char*)lineBuffer, "{"))
{
/* array of constructed data attribtute */
currentModelNode = ModelNode_getChildWithIdx(currentArrayNode, arrayIndex);
case IEC61850_FLOAT64:
{
double doubleValue;
if (sscanf(valueIndicator + 1, "%lf", &doubleValue) != 1) goto exit_error;
dataAttribute->mmsValue = MmsValue_newDouble(doubleValue);
if (currentModelNode) {
inArrayElement = true;
}
break;
case IEC61850_BOOLEAN:
{
int boolean;
if (sscanf(valueIndicator + 1, "%i", &boolean) != 1) goto exit_error;
dataAttribute->mmsValue = MmsValue_newBoolean((bool) boolean);
else {
goto exit_error;
}
break;
case IEC61850_OPTFLDS:
{
int value;
if (sscanf(valueIndicator + 1, "%i", &value) != 1) goto exit_error;
dataAttribute->mmsValue = MmsValue_newBitString(-10);
MmsValue_setBitStringFromIntegerBigEndian(dataAttribute->mmsValue, value);
}
}
else if (currentArrayNode->modelType == DataObjectModelType)
{
if (StringUtils_endsWith((char*)lineBuffer, "{"))
{
/* array of constructed data attribtute */
currentModelNode = ModelNode_getChildWithIdx(currentArrayNode, arrayIndex);
if (currentModelNode) {
inArrayElement = true;
}
break;
case IEC61850_TRGOPS:
{
int value;
if (sscanf(valueIndicator + 1, "%i", &value) != 1) goto exit_error;
dataAttribute->mmsValue = MmsValue_newBitString(-6);
MmsValue_setBitStringFromIntegerBigEndian(dataAttribute->mmsValue, value);
else {
goto exit_error;
}
break;
}
else
{
if (DEBUG_IED_SERVER)
printf("Unexpected character at end of line: %s\n", lineBuffer);
goto exit_error;
}
}
}
else if (StringUtils_startsWith((char*) lineBuffer, "DA"))
{
int arrayElements = 0;
int attributeType = 0;
int functionalConstraint = 0;
int triggerOptions = 0;
uint32_t sAddr = 0;
default:
break;
if (sscanf((char*)lineBuffer, "DA(%129s %i %i %i %i %u)", nameString, &arrayElements, &attributeType, &functionalConstraint, &triggerOptions, &sAddr) != 6) {
goto exit_error;
}
}
DataAttribute* dataAttribute = DataAttribute_create(nameString, currentModelNode,
(DataAttributeType) attributeType, (FunctionalConstraint) functionalConstraint, triggerOptions, arrayElements, sAddr);
if (arrayElements > 0)
{
inArray = true;
currentArrayNode = (ModelNode*)dataAttribute;
}
setValue((char*)lineBuffer, dataAttribute);
int lineLength = (int) strlen((char*) lineBuffer);
if (lineBuffer[lineLength - 1] == '{') {
if (lineBuffer[lineLength - 1] == '{')
{
indendation++;
currentModelNode = (ModelNode*) dataAttribute;
}
}
else if (StringUtils_startsWith((char*) lineBuffer, "DE")) {
else if (StringUtils_startsWith((char*) lineBuffer, "DE"))
{
char* start = strchr((char*) lineBuffer, '(');
if (start) {
if (start)
{
start++;
StringUtils_copyStringMax(nameString, 130, start);
@ -491,7 +635,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
/* check for index */
char* sep = strchr(nameString, ' ');
if (sep) {
if (sep)
{
char* indexStr = sep + 1;
*sep = 0;
@ -509,7 +654,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
DataSetEntry_create(currentDataSet, nameString, indexVal, componentVal);
}
}
else if (StringUtils_startsWith((char*) lineBuffer, "PA")) {
else if (StringUtils_startsWith((char*) lineBuffer, "PA"))
{
uint32_t vlanPrio;
uint32_t vlanId;
uint32_t appId;
@ -525,7 +671,6 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
if (StringUtils_createBufferFromHexString(nameString, (uint8_t*) nameString2) != 6)
goto exit_error;
PhyComAddress* dstAddress =
PhyComAddress_create((uint8_t) vlanPrio, (uint16_t) vlanId, (uint16_t) appId,
(uint8_t*) nameString2);
@ -541,18 +686,20 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
else
goto exit_error;
}
}
else {
if (StringUtils_startsWith((char*) lineBuffer, "MODEL{")) {
else
{
if (StringUtils_startsWith((char*) lineBuffer, "MODEL{"))
{
model = IedModel_create("");
stateInModel = true;
indendation = 1;
}
else if (StringUtils_startsWith((char*) lineBuffer, "MODEL(")) {
sscanf((char*) lineBuffer, "MODEL(%129s)", nameString);
else if (StringUtils_startsWith((char*) lineBuffer, "MODEL("))
{
if (sscanf((char*)lineBuffer, "MODEL(%129s)", nameString) != 1)
goto exit_error;
terminateString(nameString, ')');
model = IedModel_create(nameString);
stateInModel = true;
@ -564,14 +711,18 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
}
}
GLOBAL_FREEMEM(lineBuffer);
return model;
exit_error:
GLOBAL_FREEMEM(lineBuffer);
if (DEBUG_IED_SERVER)
printf("IED_SERVER: error parsing line %i (indentation level = %i)\n", currentLine, indendation);
IedModel_destroy(model);
return NULL;
}

@ -1,7 +1,7 @@
/*
* dynamic_model.c
*
* Copyright 2014-2022 Michael Zillgith
* Copyright 2014-2024 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -75,10 +75,12 @@ IedModel_addDataSet(IedModel* self, DataSet* dataSet)
{
if (self->dataSets == NULL)
self->dataSets = dataSet;
else {
else
{
DataSet* lastDataSet = self->dataSets;
while (lastDataSet != NULL) {
while (lastDataSet != NULL)
{
if (lastDataSet->sibling == NULL) {
lastDataSet->sibling = dataSet;
break;
@ -94,7 +96,8 @@ IedModel_addLogicalDevice(IedModel* self, LogicalDevice* lDevice)
{
if (self->firstChild == NULL)
self->firstChild = lDevice;
else {
else
{
LogicalDevice* sibling = self->firstChild;
while (sibling->sibling != NULL)
@ -109,7 +112,8 @@ IedModel_addLog(IedModel* self, Log* log)
{
if (self->logs == NULL)
self->logs = log;
else {
else
{
Log* lastLog = self->logs;
while (lastLog->sibling != NULL)
@ -124,7 +128,8 @@ IedModel_addLogControlBlock(IedModel* self, LogControlBlock* lcb)
{
if (self->lcbs == NULL)
self->lcbs = lcb;
else {
else
{
LogControlBlock* lastLcb = self->lcbs;
while (lastLcb->sibling != NULL)
@ -139,7 +144,8 @@ IedModel_addReportControlBlock(IedModel* self, ReportControlBlock* rcb)
{
if (self->rcbs == NULL)
self->rcbs = rcb;
else {
else
{
ReportControlBlock* lastRcb = self->rcbs;
while (lastRcb->sibling != NULL)
@ -155,7 +161,8 @@ IedModel_addSettingGroupControlBlock(IedModel* self, SettingGroupControlBlock* s
{
if (self->sgcbs == NULL)
self->sgcbs = sgcb;
else {
else
{
SettingGroupControlBlock* lastSgcb = self->sgcbs;
while (lastSgcb->sibling != NULL)
@ -171,7 +178,8 @@ IedModel_addGSEControlBlock(IedModel* self, GSEControlBlock* gcb)
{
if (self->gseCBs == NULL)
self->gseCBs = gcb;
else {
else
{
GSEControlBlock* lastGcb = self->gseCBs;
while (lastGcb->sibling)
@ -187,7 +195,8 @@ IedModel_addSMVControlBlock(IedModel* self, SVControlBlock* smvcb)
if (self->svCBs == NULL) {
self->svCBs = smvcb;
}
else {
else
{
SVControlBlock* lastSvCB = self->svCBs;
while (lastSvCB->sibling)
@ -202,7 +211,8 @@ LogicalDevice_createEx(const char* inst, IedModel* parent, const char* ldName)
{
LogicalDevice* self = (LogicalDevice*) GLOBAL_CALLOC(1, sizeof(LogicalDevice));
if (self) {
if (self)
{
self->name = StringUtils_copyString(inst);
self->modelType = LogicalDeviceModelType;
self->parent = (ModelNode*) parent;
@ -257,7 +267,8 @@ LogicalNode_create(const char* name, LogicalDevice* parent)
{
LogicalNode* self = (LogicalNode*) GLOBAL_MALLOC(sizeof(LogicalNode));
if (self) {
if (self)
{
self->name = StringUtils_copyString(name);
self->parent = (ModelNode*) parent;
self->modelType = LogicalNodeModelType;
@ -311,7 +322,8 @@ Log_create(const char* name, LogicalNode* parent)
{
Log* self = (Log*) GLOBAL_MALLOC(sizeof(Log));
if (self) {
if (self)
{
self->name = StringUtils_copyString(name);
self->parent = parent;
self->sibling = NULL;
@ -336,7 +348,8 @@ LogControlBlock_create(const char* name, LogicalNode* parent, const char* dataSe
{
LogControlBlock* self = (LogControlBlock*) GLOBAL_MALLOC(sizeof(LogControlBlock));
if (self) {
if (self)
{
self->name = StringUtils_copyString(name);
self->parent = parent;
self->sibling = NULL;
@ -388,7 +401,8 @@ ReportControlBlock_create(const char* name, LogicalNode* parent, const char* rpt
{
ReportControlBlock* self = (ReportControlBlock*) GLOBAL_MALLOC(sizeof(ReportControlBlock));
if (self) {
if (self)
{
self->name = StringUtils_copyString(name);
self->parent = parent;
@ -469,7 +483,8 @@ SettingGroupControlBlock_create(LogicalNode* parent, uint8_t actSG, uint8_t numO
SettingGroupControlBlock* self = (SettingGroupControlBlock*) GLOBAL_MALLOC(sizeof(SettingGroupControlBlock));
if (self) {
if (self)
{
self->parent = parent;
self->actSG = actSG;
self->numOfSGs = numOfSGs;
@ -497,7 +512,8 @@ GSEControlBlock_create(const char* name, LogicalNode* parent, const char* appId,
{
GSEControlBlock* self = (GSEControlBlock*) GLOBAL_MALLOC(sizeof(GSEControlBlock));
if (self) {
if (self)
{
self->name = StringUtils_copyString(name);
self->parent = parent;
@ -541,7 +557,8 @@ SVControlBlock_create(const char* name, LogicalNode* parent, const char* svID, c
{
SVControlBlock* self = (SVControlBlock*) GLOBAL_MALLOC(sizeof(SVControlBlock));
if (self) {
if (self)
{
self->name = StringUtils_copyString(name);
self->parent = parent;
@ -587,7 +604,8 @@ PhyComAddress_create(uint8_t vlanPriority, uint16_t vlanId, uint16_t appId, uint
{
PhyComAddress* self = (PhyComAddress*) GLOBAL_MALLOC(sizeof(PhyComAddress));
if (self) {
if (self)
{
self->vlanPriority = vlanPriority;
self->vlanId = vlanId;
self->appId = appId;
@ -630,7 +648,8 @@ DataObject_create(const char* name, ModelNode* parent, int arrayElements)
{
DataObject* self = (DataObject*) GLOBAL_MALLOC(sizeof(DataObject));
if (self) {
if (self)
{
self->name = StringUtils_copyString(name);
self->modelType = DataObjectModelType;
self->firstChild = NULL;
@ -640,21 +659,23 @@ DataObject_create(const char* name, ModelNode* parent, int arrayElements)
self->elementCount = arrayElements;
self->arrayIndex = -1;
if (arrayElements > 0) {
if (arrayElements > 0)
{
int i;
for (i = 0; i < arrayElements; i++) {
for (i = 0; i < arrayElements; i++)
{
DataObject* arrayElement = (DataObject*) GLOBAL_MALLOC(sizeof(DataObject));
if (self) {
self->name = NULL;
self->modelType = DataObjectModelType;
self->firstChild = NULL;
self->parent = parent;
self->sibling = NULL;
if (arrayElement) {
arrayElement->name = NULL;
arrayElement->modelType = DataObjectModelType;
arrayElement->firstChild = NULL;
arrayElement->parent = (ModelNode*) self;
arrayElement->sibling = NULL;
self->elementCount = 0;
self->arrayIndex = i;
arrayElement->elementCount = 0;
arrayElement->arrayIndex = i;
DataObject_addChild(self, (ModelNode*) arrayElement);
}
@ -703,7 +724,8 @@ DataAttribute_create(const char* name, ModelNode* parent, DataAttributeType type
{
DataAttribute* self = (DataAttribute*) GLOBAL_MALLOC(sizeof(DataAttribute));
if (self) {
if (self)
{
self->name = StringUtils_copyString(name);
self->elementCount = arrayElements;
self->arrayIndex = -1;
@ -717,13 +739,16 @@ DataAttribute_create(const char* name, ModelNode* parent, DataAttributeType type
self->triggerOptions = triggerOptions;
self->sAddr = sAddr;
if (arrayElements > 0) {
if (arrayElements > 0)
{
int i;
for (i = 0; i < arrayElements; i++) {
for (i = 0; i < arrayElements; i++)
{
DataAttribute* arrayElement = (DataAttribute*) GLOBAL_MALLOC(sizeof(DataAttribute));
if (arrayElement) {
if (arrayElement)
{
arrayElement->name = NULL;
arrayElement->elementCount = 0;
arrayElement->arrayIndex = i;
@ -785,7 +810,8 @@ DataSet_create(const char* name, LogicalNode* parent)
{
DataSet* self = (DataSet*) GLOBAL_MALLOC(sizeof(DataSet));
if (self) {
if (self)
{
LogicalDevice* ld = (LogicalDevice*) parent->parent;
self->name = StringUtils_createString(3, parent->name, "$", name);
@ -831,11 +857,12 @@ DataSet_addEntry(DataSet* self, DataSetEntry* newEntry)
if (self->fcdas == NULL)
self->fcdas = newEntry;
else {
else
{
DataSetEntry* lastEntry = self->fcdas;
while (lastEntry != NULL) {
while (lastEntry != NULL)
{
if (lastEntry->sibling == NULL) {
lastEntry->sibling = newEntry;
break;
@ -851,21 +878,24 @@ DataSetEntry_create(DataSet* dataSet, const char* variable, int index, const cha
{
DataSetEntry* self = (DataSetEntry*) GLOBAL_MALLOC(sizeof(DataSetEntry));
if (self) {
if (self)
{
char variableName[130];
StringUtils_copyStringMax(variableName, 130, variable);
char* separator = strchr(variableName, '/');
if (separator != NULL) {
if (separator != NULL)
{
*separator = 0;
self->variableName = StringUtils_copyString(separator + 1);
self->logicalDeviceName = StringUtils_copyString(variableName);
self->isLDNameDynamicallyAllocated = true;
}
else {
else
{
self->variableName = StringUtils_copyString(variable);
self->logicalDeviceName = dataSet->logicalDeviceName;
self->isLDNameDynamicallyAllocated = false;
@ -891,14 +921,15 @@ DataSetEntry_create(DataSet* dataSet, const char* variable, int index, const cha
static void
ModelNode_destroy(ModelNode* modelNode)
{
if (modelNode) {
if (modelNode)
{
if (modelNode->name)
GLOBAL_FREEMEM(modelNode->name);
ModelNode* currentChild = modelNode->firstChild;
while (currentChild != NULL) {
while (currentChild != NULL)
{
ModelNode* nextChild = currentChild->sibling;
ModelNode_destroy(currentChild);
@ -906,7 +937,8 @@ ModelNode_destroy(ModelNode* modelNode)
currentChild = nextChild;
}
if (modelNode->modelType == DataAttributeModelType) {
if (modelNode->modelType == DataAttributeModelType)
{
DataAttribute* dataAttribute = (DataAttribute*) modelNode;
if (dataAttribute->mmsValue != NULL) {
@ -929,8 +961,8 @@ IedModel_destroy(IedModel* model)
LogicalDevice* ld = model->firstChild;
while (ld != NULL) {
while (ld != NULL)
{
if (ld->name)
GLOBAL_FREEMEM(ld->name);
@ -939,7 +971,8 @@ IedModel_destroy(IedModel* model)
LogicalNode* ln = (LogicalNode*) ld->firstChild;
while (ln != NULL) {
while (ln != NULL)
{
GLOBAL_FREEMEM(ln->name);
/* delete all data objects */
@ -960,7 +993,6 @@ IedModel_destroy(IedModel* model)
GLOBAL_FREEMEM(currentLn);
}
LogicalDevice* currentLd = ld;
ld = (LogicalDevice*) ld->sibling;
@ -1092,4 +1124,3 @@ IedModel_destroy(IedModel* model)
GLOBAL_FREEMEM(model);
}
}

@ -538,19 +538,34 @@ static int
createObjectReference(ModelNode* node, char* objectReference, int bufSize, bool withoutIedName)
{
int bufPos;
int arrayIndex = -1;
if (node->modelType != LogicalNodeModelType) {
if (node->modelType != LogicalNodeModelType)
{
bufPos = createObjectReference(node->parent, objectReference, bufSize, withoutIedName);
if (node->modelType == DataAttributeModelType)
{
arrayIndex = ((DataAttribute*)(node))->arrayIndex;
}
else if (node->modelType == DataObjectModelType)
{
arrayIndex = ((DataObject*)(node))->arrayIndex;
}
if (bufPos == -1)
return -1;
if (bufPos < bufSize)
objectReference[bufPos++] = '.';
else
return -1;
if (arrayIndex < 0)
{
if (bufPos < bufSize)
objectReference[bufPos++] = '.';
else
return -1;
}
}
else {
else
{
LogicalNode* lNode = (LogicalNode*) node;
LogicalDevice* lDevice = (LogicalDevice*) lNode->parent;
@ -559,12 +574,13 @@ createObjectReference(ModelNode* node, char* objectReference, int bufSize, bool
bufPos = 0;
if (withoutIedName) {
if (withoutIedName)
{
objectReference[0] = 0;
StringUtils_appendString(objectReference, bufSize, lDevice->name);
}
else {
else
{
if (lDevice->ldName) {
StringUtils_copyStringMax(objectReference, bufSize, lDevice->ldName);
}
@ -581,20 +597,49 @@ createObjectReference(ModelNode* node, char* objectReference, int bufSize, bool
return -1;
}
/* append own name */
int nameLength = strlen(node->name);
if (node->name)
{
/* append own name */
int nameLength = strlen(node->name);
if (bufPos + nameLength < bufSize)
{
int i;
for (i = 0; i < nameLength; i++) {
objectReference[bufPos++] = node->name[i];
}
if (bufPos + nameLength < bufSize) {
int i;
for (i = 0; i < nameLength; i++) {
objectReference[bufPos++] = node->name[i];
return bufPos;
}
else {
return -1;
}
return bufPos;
}
else {
return -1;
if (arrayIndex > -1)
{
char arrayIndexStr[11];
snprintf(arrayIndexStr, 11, "%d", arrayIndex);
int arrayIndexStrLength = strlen(arrayIndexStr);
if (bufPos + arrayIndexStrLength + 2 < bufSize)
{
int i;
objectReference[bufPos++] = '(';
for (i = 0; i < arrayIndexStrLength; i++) {
objectReference[bufPos++] = arrayIndexStr[i];
}
objectReference[bufPos++] = ')';
}
else
return -1;
}
return bufPos;
}
char*
@ -608,15 +653,18 @@ ModelNode_getObjectReferenceEx(ModelNode* node, char* objectReference, bool with
{
bool allocated = false;
if (objectReference == NULL) {
if (objectReference == NULL)
{
objectReference = (char*) GLOBAL_MALLOC(130);
allocated = true;
}
if (objectReference) {
if (objectReference)
{
int bufPos = createObjectReference(node, objectReference, 130, withoutIedName);
if (bufPos == -1) {
if (bufPos == -1)
{
if (allocated)
GLOBAL_FREEMEM(objectReference);
@ -729,16 +777,16 @@ ModelNode_getChild(ModelNode* self, const char* name)
ModelNode* matchingNode = NULL;
while (nextNode) {
while (nextNode)
{
if (nextNode->name == NULL) {
break; /* is an array element */
}
int nodeNameLen = strlen(nextNode->name);
if (nodeNameLen == nameElementLength) {
if (nodeNameLen == nameElementLength)
{
if (memcmp(nextNode->name, name, nodeNameLen) == 0) {
matchingNode = nextNode;
break;
@ -760,12 +808,14 @@ ModelNode_getChildWithIdx(ModelNode* self, int idx)
{
ModelNode* foundElement = NULL;
if (self->modelType == DataObjectModelType || self->modelType == DataAttributeModelType) {
if (self->modelType == DataObjectModelType || self->modelType == DataAttributeModelType)
{
ModelNode* nextNode = self->firstChild;
int currentIdx = 0;
while (nextNode) {
while (nextNode)
{
if (currentIdx == idx) {
foundElement = nextNode;
break;
@ -797,14 +847,17 @@ ModelNode_getChildWithFc(ModelNode* self, const char* name, FunctionalConstraint
ModelNode* matchingNode = NULL;
while (nextNode != NULL) {
while (nextNode != NULL)
{
int nodeNameLen = strlen(nextNode->name);
if (nodeNameLen == nameElementLength) {
if (memcmp(nextNode->name, name, nodeNameLen) == 0) {
if (nodeNameLen == nameElementLength)
{
if (memcmp(nextNode->name, name, nodeNameLen) == 0)
{
if (separator == NULL) {
if (nextNode->modelType == DataAttributeModelType) {
if (nextNode->modelType == DataAttributeModelType)
{
DataAttribute* da = (DataAttribute*) nextNode;
if (da->fc == fc) {
@ -813,9 +866,10 @@ ModelNode_getChildWithFc(ModelNode* self, const char* name, FunctionalConstraint
}
}
}
else {
if (nextNode->modelType == DataAttributeModelType) {
else
{
if (nextNode->modelType == DataAttributeModelType)
{
DataAttribute* da = (DataAttribute*) nextNode;
if (da->fc == fc) {

@ -160,6 +160,16 @@ MmsConnection_setFilestoreBasepath(MmsConnection self, const char* basepath);
LIB61850_API void
MmsConnection_setRequestTimeout(MmsConnection self, uint32_t timeoutInMs);
/**
* \brief Set the maximum number outstanding calls allowed for this connection
*
* \param self MmsConnection instance to operate on
* \param calling the maximum outstanding calls allowed by the caller (client)
* \param called the maximum outstanding calls allowed by the called endpoint (server)
*/
LIB61850_API void
MmsConnnection_setMaxOutstandingCalls(MmsConnection self, int calling, int called);
/**
* \brief Get the request timeout in ms for this connection
*

@ -95,6 +95,8 @@ struct sMmsConnection {
Semaphore outstandingCallsLock;
MmsOutstandingCall outstandingCalls;
int maxOutstandingCalled;
int maxOutstandingCalling;
uint32_t requestTimeout;
uint32_t connectTimeout;

@ -1,7 +1,7 @@
/*
* mms_common_internal.h
*
* Copyright 2013-2019 Michael Zillgith
* Copyright 2013-2024 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -30,10 +30,10 @@
#include "byte_buffer.h"
#include "mms_server.h"
#define DEFAULT_MAX_SERV_OUTSTANDING_CALLING 5
#define DEFAULT_MAX_SERV_OUTSTANDING_CALLED 5
#define DEFAULT_DATA_STRUCTURE_NESTING_LEVEL 10
typedef struct sMmsOutstandingCall* MmsOutstandingCall;
#if (MMS_FILE_SERVICE == 1)
#ifndef CONFIG_MMS_MAX_NUMBER_OF_OPEN_FILES_PER_CONNECTION
@ -42,8 +42,6 @@
#include "hal_filesystem.h"
typedef struct sMmsOutstandingCall* MmsOutstandingCall;
typedef struct {
int32_t frsmId;
uint32_t readPosition;

@ -399,7 +399,7 @@ mmsServer_isAccessToArrayComponent(AlternateAccess_t* alternateAccess);
LIB61850_INTERNAL MmsValue*
mmsServer_getComponentOfArrayElement(AlternateAccess_t* alternateAccess, MmsVariableSpecification* namedVariable,
MmsValue* structuredValue);
MmsValue* structuredValue, char* componentId);
LIB61850_INTERNAL int
mmsServer_getLowIndex(AlternateAccess_t* alternateAccess);
@ -417,6 +417,10 @@ LIB61850_INTERNAL MmsDataAccessError
mmsServer_setValue(MmsServer self, MmsDomain* domain, char* itemId, MmsValue* value,
MmsServerConnection connection);
LIB61850_INTERNAL MmsDataAccessError
mmsServer_setValueEx(MmsServer self, MmsDomain* domain, char* itemId, MmsValue* value,
MmsServerConnection connection, int arrayIdx, const char* componentId);
/**
* \brief Get the current value of a variable in the server data model
*

@ -34,7 +34,7 @@ typedef MmsDataAccessError (*MmsReadAccessHandler) (void* parameter, MmsDomain*
char* variableId, MmsServerConnection connection, bool isDirectAccess);
typedef MmsDataAccessError (*MmsWriteVariableHandler)(void* parameter,
MmsDomain* domain, char* variableId, MmsValue* value,
MmsDomain* domain, const char* variableId, int arrayIdx, const char* componentId, MmsValue* value,
MmsServerConnection connection);
typedef bool (*MmsListAccessHandler) (void* parameter, MmsGetNameListType listType, MmsDomain* domain,

@ -1,7 +1,7 @@
/*
* acse.c
*
* Copyright 2013 Michael Zillgith
* Copyright 2013-2024 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -43,9 +43,10 @@ checkAuthMechanismName(uint8_t* authMechanism, int authMechLen)
{
AcseAuthenticationMechanism authenticationMechanism = ACSE_AUTH_NONE;
if (authMechanism != NULL) {
if (authMechLen == 3) {
if (authMechanism != NULL)
{
if (authMechLen == 3)
{
if (memcmp(auth_mech_password_oid, authMechanism, 3) == 0) {
authenticationMechanism = ACSE_AUTH_PASSWORD;
}
@ -64,11 +65,13 @@ authenticateClient(AcseConnection* self, AcseAuthenticationMechanism mechanism,
authParameter->mechanism = mechanism;
if (mechanism == ACSE_AUTH_PASSWORD) {
if (mechanism == ACSE_AUTH_PASSWORD)
{
authParameter->value.password.octetString = authValue;
authParameter->value.password.passwordLength = authValueLen;
}
else if (mechanism == ACSE_AUTH_TLS) {
else if (mechanism == ACSE_AUTH_TLS)
{
authParameter->value.certificate.buf = authValue;
authParameter->value.certificate.length = authValueLen;
}
@ -81,15 +84,15 @@ checkAuthentication(AcseConnection* self, uint8_t* authMechanism, int authMechLe
{
self->securityToken = NULL;
if (self->authenticator != NULL) {
if (self->authenticator != NULL)
{
AcseAuthenticationMechanism mechanism = checkAuthMechanismName(authMechanism, authMechLen);
if (mechanism == ACSE_AUTH_NONE) {
if (mechanism == ACSE_AUTH_NONE)
{
#if (CONFIG_MMS_SUPPORT_TLS == 1)
if (self->tlsSocket) {
if (self->tlsSocket)
{
int certLen;
uint8_t* certBuf = TLSSocket_getPeerCertificate(self->tlsSocket, &certLen);
@ -120,13 +123,18 @@ parseUserInformation(AcseConnection* self, uint8_t* buffer, int bufPos, int maxB
bool hasindirectReference = false;
bool isDataValid = false;
while (bufPos < maxBufPos) {
while (bufPos < maxBufPos)
{
uint8_t tag = buffer[bufPos++];
int len;
bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos);
if (bufPos < 0) {
if (len == 0)
continue;
if ((bufPos < 0) || (bufPos + len > maxBufPos))
{
*userInfoValid = false;
return -1;
}
@ -155,7 +163,8 @@ parseUserInformation(AcseConnection* self, uint8_t* buffer, int bufPos, int maxB
}
}
if (DEBUG_ACSE) {
if (DEBUG_ACSE)
{
if (!hasindirectReference)
printf("ACSE: User data has no indirect reference!\n");
@ -181,13 +190,22 @@ parseAarePdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos)
uint32_t result = 99;
while (bufPos < maxBufPos) {
while (bufPos < maxBufPos)
{
uint8_t tag = buffer[bufPos++];
int len;
bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos);
if (bufPos < 0)
if (len == 0)
continue;
if ((bufPos < 0) || (bufPos + len > maxBufPos))
{
if (DEBUG_ACSE)
printf("ACSE: Invalid PDU!\n");
return ACSE_ERROR;
}
switch (tag)
{
@ -212,12 +230,14 @@ parseAarePdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos)
break;
case 0xbe: /* user information */
if (buffer[bufPos] != 0x28) {
if (buffer[bufPos] != 0x28)
{
if (DEBUG_ACSE)
printf("ACSE: invalid user info\n");
bufPos += len;
}
else {
else
{
bufPos++;
bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos);
@ -263,13 +283,18 @@ parseAarqPdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos)
int authMechLen = 0;
bool userInfoValid = false;
while (bufPos < maxBufPos) {
while (bufPos < maxBufPos)
{
uint8_t tag = buffer[bufPos++];
int len;
bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos);
if (bufPos < 0) {
if (len == 0)
continue;
if ((bufPos < 0) || (bufPos + len > maxBufPos))
{
if (DEBUG_ACSE)
printf("ACSE: Invalid PDU!\n");
return ACSE_ASSOCIATE_FAILED;
@ -290,7 +315,9 @@ parseAarqPdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos)
case 0xa6: /* calling AP title */
{
if (buffer[bufPos] == 0x06) { /* ap-title-form2 */
if (buffer[bufPos] == 0x06)
{
/* ap-title-form2 */
int innerLength = buffer[bufPos + 1];
@ -303,7 +330,9 @@ parseAarqPdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos)
case 0xa7: /* calling AE qualifier */
{
if (buffer[bufPos] == 0x02) { /* ae-qualifier-form2 */
if (buffer[bufPos] == 0x02)
{
/* ae-qualifier-form2 */
int innerLength = buffer[bufPos + 1];
@ -328,7 +357,8 @@ parseAarqPdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos)
bufPos++;
bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos);
if (bufPos < 0) {
if (bufPos < 0)
{
if (DEBUG_ACSE)
printf("ACSE: Invalid PDU!\n");
return ACSE_ASSOCIATE_FAILED;
@ -340,17 +370,20 @@ parseAarqPdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos)
break;
case 0xbe: /* user information */
if (buffer[bufPos] != 0x28) {
if (buffer[bufPos] != 0x28)
{
if (DEBUG_ACSE)
printf("ACSE: invalid user info\n");
bufPos += len;
}
else {
else
{
bufPos++;
bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos);
if (bufPos < 0) {
if (bufPos < 0)
{
if (DEBUG_ACSE)
printf("ACSE: Invalid PDU!\n");
return ACSE_ASSOCIATE_FAILED;
@ -358,7 +391,8 @@ parseAarqPdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos)
bufPos = parseUserInformation(self, buffer, bufPos, bufPos + len, &userInfoValid);
if (bufPos < 0) {
if (bufPos < 0)
{
if (DEBUG_ACSE)
printf("ACSE: Invalid PDU!\n");
return ACSE_ASSOCIATE_FAILED;
@ -378,14 +412,16 @@ parseAarqPdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos)
}
}
if (checkAuthentication(self, authMechanism, authMechLen, authValue, authValueLen) == false) {
if (checkAuthentication(self, authMechanism, authMechLen, authValue, authValueLen) == false)
{
if (DEBUG_ACSE)
printf("ACSE: parseAarqPdu: check authentication failed!\n");
return ACSE_ASSOCIATE_FAILED;
}
if (userInfoValid == false) {
if (userInfoValid == false)
{
if (DEBUG_ACSE)
printf("ACSE: parseAarqPdu: user info invalid!\n");
@ -420,6 +456,14 @@ AcseConnection_parseMessage(AcseConnection* self, ByteBuffer* message)
{
AcseIndication indication = ACSE_ERROR;
if (message == NULL || message->size < 1)
{
if (DEBUG_ACSE)
printf("ACSE: invalid message - no payload\n");
return ACSE_ERROR;
}
uint8_t* buffer = message->buffer;
int messageSize = message->size;
@ -826,4 +870,3 @@ AcseConnection_createReleaseResponseMessage(AcseConnection* self, BufferChain wr
writeBuffer->length = 2;
writeBuffer->nextPart = NULL;
}

@ -3,7 +3,7 @@
*
* Client side representation of the ISO stack (COTP, session, presentation, ACSE)
*
* Copyright 2013-2022 Michael Zillgith
* Copyright 2013-2024 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -146,7 +146,8 @@ IsoClientConnection_create(IsoConnectionParameters parameters, IsoIndicationCall
{
IsoClientConnection self = (IsoClientConnection) GLOBAL_CALLOC(1, sizeof(struct sIsoClientConnection));
if (self) {
if (self)
{
self->parameters = parameters;
self->callback = callback;
self->callbackParameter = callbackParameter;
@ -196,7 +197,8 @@ sendConnectionRequestMessage(IsoClientConnection self)
int socketExtensionBufferSize = CONFIG_MMS_MAXIMUM_PDU_SIZE + 1000;
uint8_t* socketExtensionBuffer = NULL;
if (self->cotpConnection) {
if (self->cotpConnection)
{
/* Destroy existing handle set when connection is reused */
if (self->cotpConnection->handleSet)
Handleset_destroy(self->cotpConnection->handleSet);
@ -205,19 +207,20 @@ sendConnectionRequestMessage(IsoClientConnection self)
socketExtensionBuffer = self->cotpConnection->socketExtensionBuffer;
}
if (socketExtensionBuffer == NULL) {
if (socketExtensionBuffer == NULL)
{
socketExtensionBuffer = (uint8_t*)GLOBAL_MALLOC(socketExtensionBufferSize);
}
if (socketExtensionBuffer) {
if (socketExtensionBuffer)
{
/* COTP (ISO transport) handshake */
CotpConnection_init(self->cotpConnection, self->socket, self->receiveBuffer, self->cotpReadBuffer, self->cotpWriteBuffer,
socketExtensionBuffer, socketExtensionBufferSize);
#if (CONFIG_MMS_SUPPORT_TLS == 1)
if (self->parameters->tlsConfiguration) {
if (self->parameters->tlsConfiguration)
{
TLSConfiguration_setClientMode(self->parameters->tlsConfiguration);
/* create TLSSocket and start TLS authentication */
@ -225,8 +228,8 @@ sendConnectionRequestMessage(IsoClientConnection self)
if (tlsSocket)
self->cotpConnection->tlsSocket = tlsSocket;
else {
else
{
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: TLS handshake failed!\n");
@ -244,7 +247,8 @@ sendConnectionRequestMessage(IsoClientConnection self)
else
return true;
}
else {
else
{
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: Failed to allocate socket extension buffer\n");
@ -298,14 +302,14 @@ sendAcseInitiateRequest(IsoClientConnection self)
Semaphore_post(self->transmitBufferMutex);
}
static void
releaseSocket(IsoClientConnection self)
{
if (self->socket) {
if (self->socket)
{
#if (CONFIG_MMS_SUPPORT_TLS == 1)
if (self->cotpConnection->tlsSocket) {
if (self->cotpConnection->tlsSocket)
{
TLSSocket_close(self->cotpConnection->tlsSocket);
self->cotpConnection->tlsSocket = NULL;
}
@ -345,29 +349,34 @@ IsoClientConnection_handleConnection(IsoClientConnection self)
{
SocketState socketState = Socket_checkAsyncConnectState(self->socket);
if (socketState == SOCKET_STATE_CONNECTED) {
if (sendConnectionRequestMessage(self)) {
if (socketState == SOCKET_STATE_CONNECTED)
{
if (sendConnectionRequestMessage(self))
{
self->nextReadTimeout = Hal_getTimeInMs() + self->readTimeoutInMs;
nextState = INT_STATE_WAIT_FOR_COTP_CONNECT_RESP;
}
else {
else
{
IsoClientConnection_releaseTransmitBuffer(self);
self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL);
nextState = INT_STATE_CLOSE_ON_ERROR;
}
}
else if (socketState == SOCKET_STATE_FAILED) {
else if (socketState == SOCKET_STATE_FAILED)
{
IsoClientConnection_releaseTransmitBuffer(self);
self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL);
nextState = INT_STATE_CLOSE_ON_ERROR;
}
else {
else
{
/* check connect timeout */
uint64_t currentTime = Hal_getTimeInMs();
if (currentTime > self->nextReadTimeout) {
if (currentTime > self->nextReadTimeout)
{
IsoClientConnection_releaseTransmitBuffer(self);
self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL);
nextState = INT_STATE_CLOSE_ON_ERROR;
@ -385,8 +394,8 @@ IsoClientConnection_handleConnection(IsoClientConnection self)
{
uint64_t currentTime = Hal_getTimeInMs();
if (currentTime > self->nextReadTimeout) {
if (currentTime > self->nextReadTimeout)
{
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: Timeout waiting for COTP CR\n");
@ -396,15 +405,16 @@ IsoClientConnection_handleConnection(IsoClientConnection self)
nextState = INT_STATE_CLOSE_ON_ERROR;
}
else {
else
{
TpktState packetState = CotpConnection_readToTpktBuffer(self->cotpConnection);
if (packetState == TPKT_PACKET_COMPLETE) {
if (packetState == TPKT_PACKET_COMPLETE)
{
CotpIndication cotpIndication = CotpConnection_parseIncomingMessage(self->cotpConnection);
if (cotpIndication != COTP_CONNECT_INDICATION) {
if (cotpIndication != COTP_CONNECT_INDICATION)
{
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: Unexpected COTP state (%i)\n", cotpIndication);
@ -414,7 +424,8 @@ IsoClientConnection_handleConnection(IsoClientConnection self)
nextState = INT_STATE_CLOSE_ON_ERROR;
}
else {
else
{
sendAcseInitiateRequest(self);
self->nextReadTimeout = Hal_getTimeInMs() + self->readTimeoutInMs;
@ -422,7 +433,8 @@ IsoClientConnection_handleConnection(IsoClientConnection self)
nextState = INT_STATE_WAIT_FOR_ACSE_RESP;
}
}
else if (packetState == TPKT_ERROR) {
else if (packetState == TPKT_ERROR)
{
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: Error receiving COTP message\n");
@ -435,7 +447,6 @@ IsoClientConnection_handleConnection(IsoClientConnection self)
else {
waits = true;
}
}
}
break;
@ -444,8 +455,8 @@ IsoClientConnection_handleConnection(IsoClientConnection self)
{
uint64_t currentTime = Hal_getTimeInMs();
if (currentTime > self->nextReadTimeout) {
if (currentTime > self->nextReadTimeout)
{
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: Timeout waiting for ACSE initiate response\n");
@ -453,15 +464,16 @@ IsoClientConnection_handleConnection(IsoClientConnection self)
nextState = INT_STATE_CLOSE_ON_ERROR;
}
else {
else
{
TpktState packetState = CotpConnection_readToTpktBuffer(self->cotpConnection);
if (packetState == TPKT_PACKET_COMPLETE) {
if (packetState == TPKT_PACKET_COMPLETE)
{
CotpIndication cotpIndication = CotpConnection_parseIncomingMessage(self->cotpConnection);
if (cotpIndication != COTP_DATA_INDICATION) {
if (cotpIndication != COTP_DATA_INDICATION)
{
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: Unexpected COTP state (%i)\n", cotpIndication);
@ -469,67 +481,71 @@ IsoClientConnection_handleConnection(IsoClientConnection self)
nextState = INT_STATE_CLOSE_ON_ERROR;
}
else {
else
{
/* parse ACSE response */
IsoSessionIndication sessionIndication;
sessionIndication =
IsoSession_parseMessage(self->session, CotpConnection_getPayload(self->cotpConnection));
IsoSessionIndication sessionIndication;
if (sessionIndication != SESSION_CONNECT) {
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: IsoClientConnection_associate: no session connect indication\n");
sessionIndication =
IsoSession_parseMessage(self->session, CotpConnection_getPayload(self->cotpConnection));
self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL);
nextState = INT_STATE_CLOSE_ON_ERROR;
}
else {
if (IsoPresentation_parseAcceptMessage(self->presentation, IsoSession_getUserData(self->session)) == false) {
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: IsoClientConnection_associate: no presentation ok indication\n");
self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL);
nextState = INT_STATE_CLOSE_ON_ERROR;
}
else {
AcseIndication acseIndication = AcseConnection_parseMessage(&(self->acseConnection), &self->presentation->nextPayload);
if (acseIndication != ACSE_ASSOCIATE) {
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: IsoClientConnection_associate: no ACSE_ASSOCIATE indication\n");
self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL);
nextState = INT_STATE_CLOSE_ON_ERROR;
}
else {
ByteBuffer_wrap(self->receivePayloadBuffer, self->acseConnection.userDataBuffer,
self->acseConnection.userDataBufferSize, self->acseConnection.userDataBufferSize);
if (sessionIndication != SESSION_CONNECT)
{
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: IsoClientConnection_associate: no session connect indication\n");
setState(self, STATE_CONNECTED);
nextState = INT_STATE_WAIT_FOR_DATA_MSG;
self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL);
if (self->callback(ISO_IND_ASSOCIATION_SUCCESS, self->callbackParameter, self->receivePayloadBuffer) == false) {
nextState = INT_STATE_CLOSE_ON_ERROR;
}
nextState = INT_STATE_CLOSE_ON_ERROR;
}
else
{
if (IsoPresentation_parseAcceptMessage(self->presentation, IsoSession_getUserData(self->session)) == false)
{
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: no presentation accept indication\n");
}
self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL);
}
nextState = INT_STATE_CLOSE_ON_ERROR;
}
else
{
AcseIndication acseIndication = AcseConnection_parseMessage(&(self->acseConnection), &self->presentation->nextPayload);
if (acseIndication != ACSE_ASSOCIATE)
{
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: no ACSE_ASSOCIATE indication\n");
self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL);
nextState = INT_STATE_CLOSE_ON_ERROR;
}
else
{
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: ACSE AARE - association accepted\n");
ByteBuffer_wrap(self->receivePayloadBuffer, self->acseConnection.userDataBuffer,
self->acseConnection.userDataBufferSize, self->acseConnection.userDataBufferSize);
setState(self, STATE_CONNECTED);
nextState = INT_STATE_WAIT_FOR_DATA_MSG;
if (self->callback(ISO_IND_ASSOCIATION_SUCCESS, self->callbackParameter, self->receivePayloadBuffer) == false) {
nextState = INT_STATE_CLOSE_ON_ERROR;
}
}
}
CotpConnection_resetPayload(self->cotpConnection);
}
CotpConnection_resetPayload(self->cotpConnection);
}
}
}
else if (packetState == TPKT_ERROR) {
else if (packetState == TPKT_ERROR)
{
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: Error receiving COTP message\n");
@ -540,7 +556,6 @@ IsoClientConnection_handleConnection(IsoClientConnection self)
else {
waits = true;
}
}
}
break;
@ -552,8 +567,8 @@ IsoClientConnection_handleConnection(IsoClientConnection self)
if (packetState == TPKT_ERROR) {
nextState = INT_STATE_CLOSE_ON_ERROR;
}
else if (packetState == TPKT_PACKET_COMPLETE) {
else if (packetState == TPKT_PACKET_COMPLETE)
{
CotpIndication cotpIndication = CotpConnection_parseIncomingMessage(self->cotpConnection);
switch (cotpIndication) {
@ -576,23 +591,24 @@ IsoClientConnection_handleConnection(IsoClientConnection self)
IsoSession_parseMessage(self->session,
CotpConnection_getPayload(self->cotpConnection));
if (sessionIndication != SESSION_DATA) {
if (sessionIndication != SESSION_DATA)
{
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT_CONNECTION: Invalid session message\n");
nextState = INT_STATE_CLOSE_ON_ERROR;
}
else {
if (!IsoPresentation_parseUserData(self->presentation, IsoSession_getUserData(self->session))) {
else
{
if (!IsoPresentation_parseUserData(self->presentation, IsoSession_getUserData(self->session)))
{
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT_CONNECTION: Invalid presentation message\n");
nextState = INT_STATE_CLOSE_ON_ERROR;
}
else {
else
{
self->callback(ISO_IND_DATA, self->callbackParameter,
&(self->presentation->nextPayload));
@ -659,7 +675,6 @@ IsoClientConnection_handleConnection(IsoClientConnection self)
return waits;
}
bool
IsoClientConnection_associateAsync(IsoClientConnection self, uint32_t connectTimeoutInMs, uint32_t readTimeoutInMs)
{
@ -669,7 +684,8 @@ IsoClientConnection_associateAsync(IsoClientConnection self, uint32_t connectTim
self->socket = TcpSocket_create();
if (self->socket == NULL) {
if (self->socket == NULL)
{
Semaphore_post(self->tickMutex);
return false;
}
@ -697,8 +713,8 @@ IsoClientConnection_associateAsync(IsoClientConnection self, uint32_t connectTim
Socket_bind(self->socket, self->parameters->localIpAddress, self->parameters->localTcpPort);
}
if (Socket_connectAsync(self->socket, self->parameters->hostname, self->parameters->tcpPort) == false) {
if (Socket_connectAsync(self->socket, self->parameters->hostname, self->parameters->tcpPort) == false)
{
Socket_destroy(self->socket);
self->socket = NULL;
@ -718,7 +734,8 @@ IsoClientConnection_associateAsync(IsoClientConnection self, uint32_t connectTim
void
IsoClientConnection_sendMessage(IsoClientConnection self, ByteBuffer* payloadBuffer)
{
if (getState(self) == STATE_CONNECTED) {
if (getState(self) == STATE_CONNECTED)
{
struct sBufferChain payloadBCMemory;
BufferChain payload = &payloadBCMemory;
@ -743,7 +760,8 @@ IsoClientConnection_sendMessage(IsoClientConnection self, ByteBuffer* payloadBuf
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: IsoClientConnection_sendMessage: send message failed!\n");
}
else {
else
{
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: Not connected --> cannot send message\n");
}
@ -762,7 +780,8 @@ IsoClientConnection_close(IsoClientConnection self)
eIsoClientInternalState intState = getIntState(self);
if ((intState != INT_STATE_IDLE) && (intState != INT_STATE_ERROR) && (intState != INT_STATE_CLOSE_ON_ERROR)) {
if ((intState != INT_STATE_IDLE) && (intState != INT_STATE_ERROR) && (intState != INT_STATE_CLOSE_ON_ERROR))
{
setIntState(self, INT_STATE_CLOSING_CONNECTION);
Semaphore_post(self->tickMutex);
@ -783,8 +802,8 @@ IsoClientConnection_destroy(IsoClientConnection self)
int state = getState(self);
if (state == STATE_CONNECTED) {
if (state == STATE_CONNECTED)
{
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: call IsoClientConnection_close\n");
@ -798,7 +817,8 @@ IsoClientConnection_destroy(IsoClientConnection self)
if (self->receiveBuffer != NULL)
GLOBAL_FREEMEM(self->receiveBuffer);
if (self->cotpConnection != NULL) {
if (self->cotpConnection != NULL)
{
if (self->cotpConnection->handleSet != NULL)
Handleset_destroy(self->cotpConnection->handleSet);
@ -910,7 +930,6 @@ IsoClientConnection_release(IsoClientConnection self)
Semaphore_post(self->transmitBufferMutex);
}
ByteBuffer*
IsoClientConnection_allocateTransmitBuffer(IsoClientConnection self)
{

File diff suppressed because it is too large Load Diff

@ -180,7 +180,6 @@ exit_reject_invalid_pdu:
mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
}
void
mmsClient_handleFileReadRequest(
MmsConnection connection,
@ -191,7 +190,7 @@ mmsClient_handleFileReadRequest(
int32_t frsmId = BerDecoder_decodeInt32(buffer, maxBufPos - bufPos, bufPos);
if (DEBUG_MMS_CLIENT)
printf("MMS_CLIENT: mmsClient_handleFileReadRequest read request for frsmId: %i\n", frsmId);
printf("MMS_CLIENT: mmsClient_handleFileReadRequest read request for frsmId: %i\n", (int)frsmId);
MmsFileReadStateMachine* frsm = getFrsm(connection, frsmId);
@ -401,7 +400,6 @@ mmsClient_createFileDirectoryRequest(uint32_t invokeId, ByteBuffer* request, con
request->size = bufPos;
}
void
mmsClient_createFileRenameRequest(uint32_t invokeId, ByteBuffer* request, const char* currentFileName, const char* newFileName)
{
@ -466,7 +464,6 @@ mmsClient_createObtainFileRequest(uint32_t invokeId, ByteBuffer* request, const
request->size = bufPos;
}
static bool
parseFileAttributes(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t* fileSize, uint64_t* lastModified)
{
@ -610,7 +607,6 @@ parseListOfDirectoryEntries(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t
return true;
}
bool
mmsClient_parseFileDirectoryResponse(ByteBuffer* response, int bufPos, uint32_t invokeId, MmsConnection_FileDirectoryHandler handler, void* parameter)
{
@ -731,16 +727,14 @@ mmsMsg_parseFileOpenResponse(uint8_t* buffer, int bufPos, int maxBufPos, int32_t
}
bool
mmsMsg_parseFileReadResponse(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t invokeId, int frsmId, bool* moreFollows, MmsConnection_FileReadHandler handler, void* handlerParameter)
mmsMsg_parseFileReadResponse(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t invokeId, int32_t frsmId, bool* moreFollows, MmsConnection_FileReadHandler handler, void* handlerParameter)
{
int length;
uint8_t* data = NULL;
int dataLen = 0;
uint8_t tag = buffer[bufPos++];
if (tag != 0xbf) {
if (DEBUG_MMS_CLIENT)
printf("MMS_CLIENT/SERVER: mmsClient_parseFileReadResponse: unknown tag %02x\n", tag);

@ -39,18 +39,16 @@ static uint8_t servicesSupported[] = { 0xee, 0x1c, 0x00, 0x00, 0x04, 0x08, 0x00,
void
mmsClient_createInitiateRequest(MmsConnection self, ByteBuffer* message)
{
int maxServerOutstandingCalling = DEFAULT_MAX_SERV_OUTSTANDING_CALLING;
int maxServerOutstandingCalled = DEFAULT_MAX_SERV_OUTSTANDING_CALLED;
int dataStructureNestingLevel = DEFAULT_DATA_STRUCTURE_NESTING_LEVEL;
uint32_t localDetailSize =
BerEncoder_UInt32determineEncodedSize(self->parameters.maxPduSize);
uint32_t proposedMaxServerOutstandingCallingSize =
BerEncoder_UInt32determineEncodedSize(maxServerOutstandingCalling);
BerEncoder_UInt32determineEncodedSize(self->maxOutstandingCalling);
uint32_t proposedMaxServerOutstandingCalledSize =
BerEncoder_UInt32determineEncodedSize(maxServerOutstandingCalled);
BerEncoder_UInt32determineEncodedSize(self->maxOutstandingCalled);
uint32_t dataStructureNestingLevelSize =
BerEncoder_UInt32determineEncodedSize(dataStructureNestingLevel);
@ -76,11 +74,11 @@ mmsClient_createInitiateRequest(MmsConnection self, ByteBuffer* message)
/* proposedMaxServerOutstandingCalling */
bufPos = BerEncoder_encodeTL(0x81, proposedMaxServerOutstandingCallingSize, buffer, bufPos);
bufPos = BerEncoder_encodeUInt32(maxServerOutstandingCalling, buffer, bufPos);
bufPos = BerEncoder_encodeUInt32(self->maxOutstandingCalling, buffer, bufPos);
/* proposedMaxServerOutstandingCalled */
bufPos = BerEncoder_encodeTL(0x82, proposedMaxServerOutstandingCalledSize, buffer, bufPos);
bufPos = BerEncoder_encodeUInt32(maxServerOutstandingCalled, buffer, bufPos);
bufPos = BerEncoder_encodeUInt32(self->maxOutstandingCalled, buffer, bufPos);
/* proposedDataStructureNestingLevel */
bufPos = BerEncoder_encodeTL(0x83, dataStructureNestingLevelSize, buffer, bufPos);
@ -169,8 +167,8 @@ mmsClient_parseInitiateResponse(MmsConnection self, ByteBuffer* response)
{
self->parameters.maxPduSize = CONFIG_MMS_MAXIMUM_PDU_SIZE;
self->parameters.dataStructureNestingLevel = DEFAULT_DATA_STRUCTURE_NESTING_LEVEL;
self->parameters.maxServOutstandingCalled = DEFAULT_MAX_SERV_OUTSTANDING_CALLED;
self->parameters.maxServOutstandingCalling = DEFAULT_MAX_SERV_OUTSTANDING_CALLING;
self->parameters.maxServOutstandingCalled = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED;
self->parameters.maxServOutstandingCalling = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING;
int bufPos = 1; /* ignore tag - already checked */
@ -203,16 +201,16 @@ mmsClient_parseInitiateResponse(MmsConnection self, ByteBuffer* response)
case 0x81: /* proposed-max-serv-outstanding-calling */
self->parameters.maxServOutstandingCalling = BerDecoder_decodeUint32(buffer, length, bufPos);
if (self->parameters.maxServOutstandingCalling > DEFAULT_MAX_SERV_OUTSTANDING_CALLING)
self->parameters.maxServOutstandingCalling = DEFAULT_MAX_SERV_OUTSTANDING_CALLING;
if (self->parameters.maxServOutstandingCalling > CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING)
self->parameters.maxServOutstandingCalling = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING;
break;
case 0x82: /* proposed-max-serv-outstanding-called */
self->parameters.maxServOutstandingCalled = BerDecoder_decodeUint32(buffer, length, bufPos);
if (self->parameters.maxServOutstandingCalled > DEFAULT_MAX_SERV_OUTSTANDING_CALLED)
self->parameters.maxServOutstandingCalled = DEFAULT_MAX_SERV_OUTSTANDING_CALLED;
if (self->parameters.maxServOutstandingCalled > CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED)
self->parameters.maxServOutstandingCalled = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED;
break;
case 0x83: /* proposed-data-structure-nesting-level */

@ -1,7 +1,7 @@
/*
* mms_type_spec.c
*
* Copyright 2013 Michael Zillgith
* Copyright 2013-2024 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -108,36 +108,40 @@ MmsVariableSpecification_getType(MmsVariableSpecification* self)
bool
MmsVariableSpecification_isValueOfType(MmsVariableSpecification* self, const MmsValue* value)
{
if ((self->type) == (value->type)) {
if ((self->type == MMS_STRUCTURE) || (self->type == MMS_ARRAY)) {
if ((self->type) == (value->type))
{
if ((self->type == MMS_STRUCTURE) || (self->type == MMS_ARRAY))
{
int componentCount = self->typeSpec.structure.elementCount;
if (componentCount != (int) MmsValue_getArraySize(value))
return false;
if (self->type == MMS_STRUCTURE) {
if (self->type == MMS_STRUCTURE)
{
int i;
for (i = 0; i < componentCount; i++) {
for (i = 0; i < componentCount; i++)
{
if (MmsVariableSpecification_isValueOfType(self->typeSpec.structure.elements[i], MmsValue_getElement(value, i)) == false)
return false;
}
return true;
}
else {
else
{
int i;
for (i = 0; i < componentCount; i++) {
for (i = 0; i < componentCount; i++)
{
if (MmsVariableSpecification_isValueOfType(self->typeSpec.array.elementTypeSpec, MmsValue_getElement(value, i)) == false)
return false;
}
return true;
}
}
else if (self->type == MMS_BIT_STRING) {
else if (self->type == MMS_BIT_STRING)
{
if (self->typeSpec.bitString == value->value.bitString.size)
return true;

@ -1463,13 +1463,15 @@ MmsValue_newOctetString(int size, int maxSize)
{
MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue));
if (self) {
if (self)
{
self->type = MMS_OCTET_STRING;
self->value.octetString.size = size;
self->value.octetString.maxSize = maxSize;
self->value.octetString.buf = (uint8_t*) GLOBAL_CALLOC(1, abs(maxSize));
if (self->value.octetString.buf == NULL) {
if ((maxSize != 0) && (self->value.octetString.buf == NULL))
{
GLOBAL_FREEMEM(self);
self = NULL;
}
@ -1677,14 +1679,16 @@ exit_function:
}
static void
setVisibleStringValue(MmsValue* self, const char* string)
setVisibleStringValue(MmsValue* self, const char* value)
{
if (self->value.visibleString.buf != NULL) {
if (string != NULL) {
int newStringSize = strlen(string);
if (self->value.visibleString.buf != NULL)
{
if (value != NULL)
{
int newStringSize = strlen(value);
if (newStringSize > self->value.visibleString.size) {
if (newStringSize > self->value.visibleString.size)
{
GLOBAL_FREEMEM(self->value.visibleString.buf);
self->value.visibleString.buf = (char*) GLOBAL_MALLOC(newStringSize + 1);
@ -1694,7 +1698,7 @@ setVisibleStringValue(MmsValue* self, const char* string)
self->value.visibleString.size = newStringSize;
}
StringUtils_copyStringMax(self->value.visibleString.buf, self->value.visibleString.size + 1, string);
StringUtils_copyStringMax(self->value.visibleString.buf, self->value.visibleString.size + 1, value);
}
else
self->value.visibleString.buf[0] = 0;
@ -1906,7 +1910,8 @@ MmsValue_newStringFromByteArray(const uint8_t* byteArray, int size, MmsType type
self->value.visibleString.buf = StringUtils_createStringFromBuffer(byteArray, size);
if (self->value.visibleString.buf == NULL) {
if (self->value.visibleString.buf == NULL)
{
GLOBAL_FREEMEM(self);
self = NULL;
}
@ -2008,17 +2013,20 @@ MmsValue_createArray(const MmsVariableSpecification* elementType, int size)
self->value.structure.size = size;
self->value.structure.components = (MmsValue**) GLOBAL_CALLOC(size, sizeof(MmsValue*));
if (self->value.structure.components == NULL) {
if (self->value.structure.components == NULL)
{
GLOBAL_FREEMEM(self);
self = NULL;
goto exit_function;
}
int i;
for (i = 0; i < size; i++) {
for (i = 0; i < size; i++)
{
self->value.structure.components[i] = MmsValue_newDefaultValue(elementType);
if (self->value.structure.components[i] == NULL) {
if (self->value.structure.components[i] == NULL)
{
MmsValue_delete(self);
self = NULL;
goto exit_function;
@ -2102,9 +2110,10 @@ MmsValue_setDeletable(MmsValue* self)
void
MmsValue_setDeletableRecursive(MmsValue* self)
{
if (self != NULL) {
if ((MmsValue_getType(self) == MMS_ARRAY) || (MmsValue_getType(self) == MMS_STRUCTURE)) {
if (self)
{
if ((MmsValue_getType(self) == MMS_ARRAY) || (MmsValue_getType(self) == MMS_STRUCTURE))
{
int i;
int elementCount = MmsValue_getArraySize(self);
@ -2179,7 +2188,8 @@ MmsValue_getTypeString(MmsValue* self)
const char*
MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize)
{
if (self == NULL) {
if (self == NULL)
{
StringUtils_copyStringMax(buffer, bufferSize, "(null)");
return buffer;
@ -2240,7 +2250,8 @@ MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize)
int size = MmsValue_getBitStringSize(self);
/* fill buffer with zeros */
if (size + 1 > bufferSize) {
if (size + 1 > bufferSize)
{
memset(buffer, 0, bufferSize);
size = bufferSize - 1;
@ -2250,7 +2261,8 @@ MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize)
}
int i;
for (i = 0; i < size; i++) {
for (i = 0; i < size; i++)
{
if (MmsValue_getBitStringBit(self, i))
buffer[bufPos++] = '1';
else
@ -2290,7 +2302,8 @@ MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize)
int size = MmsValue_getOctetStringSize(self);
int bufPos = 0;
int i;
for (i = 0; i < size; i++) {
for (i = 0; i < size; i++)
{
snprintf(buffer + bufPos, bufferSize - bufPos, "%02x", self->value.octetString.buf[i]);
bufPos += 2;

@ -171,9 +171,12 @@ MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength, int* endBu
if (bufPos < 0)
goto exit_with_error;
/* if not indefinite length end tag, data length must be > 0 */
if ((tag != 0) && (dataLength == 0))
goto exit_with_error;
/* if not indefinite length end tag, visible-string, mms-string, or octet-string, data length must be > 0 */
if (tag != 0)
{
if (tag != 0x8a && tag != 0x90 && tag != 0x89 && dataLength == 0)
goto exit_with_error;
}
switch (tag) {
@ -192,8 +195,8 @@ MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength, int* endBu
int i;
for (i = 0; i < elementCount; i++) {
for (i = 0; i < elementCount; i++)
{
int elementLength;
int newBufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos + 1, dataEndBufPos);
@ -304,7 +307,8 @@ MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength, int* endBu
break;
case 0x91: /* MMS_UTC_TIME */
if (dataLength == 8) {
if (dataLength == 8)
{
value = MmsValue_newUtcTime(0);
MmsValue_setUtcTimeByBuffer(value, buffer + bufPos);
bufPos += dataLength;

@ -330,9 +330,9 @@ parseInitiateRequestPdu(MmsServerConnection self, uint8_t* buffer, int bufPos, i
self->dataStructureNestingLevel =
DEFAULT_DATA_STRUCTURE_NESTING_LEVEL;
self->maxServOutstandingCalled = DEFAULT_MAX_SERV_OUTSTANDING_CALLED;
self->maxServOutstandingCalled = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED;
self->maxServOutstandingCalling = DEFAULT_MAX_SERV_OUTSTANDING_CALLING;
self->maxServOutstandingCalling = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING;
self->negotiatedParameterCBC[0] = 0;
self->negotiatedParameterCBC[1] = 0;
@ -367,16 +367,16 @@ parseInitiateRequestPdu(MmsServerConnection self, uint8_t* buffer, int bufPos, i
case 0x81: /* proposed-max-serv-outstanding-calling */
self->maxServOutstandingCalling = BerDecoder_decodeUint32(buffer, length, bufPos);
if (self->maxServOutstandingCalling > DEFAULT_MAX_SERV_OUTSTANDING_CALLING)
self->maxServOutstandingCalling = DEFAULT_MAX_SERV_OUTSTANDING_CALLING;
if (self->maxServOutstandingCalling > CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING)
self->maxServOutstandingCalling = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING;
break;
case 0x82: /* proposed-max-serv-outstanding-called */
self->maxServOutstandingCalled = BerDecoder_decodeUint32(buffer, length, bufPos);
if (self->maxServOutstandingCalled > DEFAULT_MAX_SERV_OUTSTANDING_CALLED)
self->maxServOutstandingCalled = DEFAULT_MAX_SERV_OUTSTANDING_CALLED;
if (self->maxServOutstandingCalled > CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED)
self->maxServOutstandingCalled = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED;
break;
case 0x83: /* proposed-data-structure-nesting-level */

@ -1,7 +1,7 @@
/*
* mms_get_var_access_service.c
*
* Copyright 2013-2023 Michael Zillgith
* Copyright 2013-2024 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -310,9 +310,12 @@ mmsServer_handleGetVariableAccessAttributesRequest(
rval = ber_decode(NULL, &asn_DEF_GetVariableAccessAttributesRequest,
(void**) &request, buffer + bufPos, maxBufPos - bufPos);
if (rval.code == RC_OK) {
if (request->present == GetVariableAccessAttributesRequest_PR_name) {
if (request->choice.name.present == ObjectName_PR_domainspecific) {
if (rval.code == RC_OK)
{
if (request->present == GetVariableAccessAttributesRequest_PR_name)
{
if (request->choice.name.present == ObjectName_PR_domainspecific)
{
Identifier_t domainId = request->choice.name.choice.domainspecific.domainId;
Identifier_t nameId = request->choice.name.choice.domainspecific.itemId;
@ -328,7 +331,8 @@ mmsServer_handleGetVariableAccessAttributesRequest(
GLOBAL_FREEMEM(nameIdStr);
}
#if (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1)
else if (request->choice.name.present == ObjectName_PR_vmdspecific) {
else if (request->choice.name.present == ObjectName_PR_vmdspecific)
{
Identifier_t nameId = request->choice.name.choice.vmdspecific;
char* nameIdStr = StringUtils_createStringFromBuffer(nameId.buf, nameId.size);
@ -357,6 +361,12 @@ mmsServer_handleGetVariableAccessAttributesRequest(
asn_DEF_GetVariableAccessAttributesRequest.free_struct(&asn_DEF_GetVariableAccessAttributesRequest, request, 0);
if (ByteBuffer_getSize(response) > connection->maxPduSize)
{
ByteBuffer_setSize(response, 0);
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_OTHER);
}
return retVal;
}

@ -130,16 +130,22 @@ mmsServer_handleDeleteNamedVariableListRequest(MmsServerConnection connection,
goto exit_function;
}
if ((mmsPdu->present == MmsPdu_PR_confirmedRequestPdu) &&
(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present
== ConfirmedServiceRequest_PR_deleteNamedVariableList))
{
request = &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.deleteNamedVariableList);
}
else {
mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
goto exit_function;
}
if ((mmsPdu->present == MmsPdu_PR_confirmedRequestPdu) &&
(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present
== ConfirmedServiceRequest_PR_deleteNamedVariableList))
{
request = &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.deleteNamedVariableList);
}
else {
mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
goto exit_function;
}
if (request->listOfVariableListName == NULL)
{
mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
goto exit_function;
}
long scopeOfDelete = DeleteNamedVariableListRequest__scopeOfDelete_specific;
@ -675,7 +681,8 @@ createGetNamedVariableListAttributesResponse(int invokeId, ByteBuffer* response,
LinkedList variable = LinkedList_getNext(variables);
int i;
for (i = 0; i < variableCount; i++) {
for (i = 0; i < variableCount; i++)
{
MmsNamedVariableListEntry variableEntry = (MmsNamedVariableListEntry) variable->data;
varListResponse->listOfVariable.list.array[i] = (struct GetNamedVariableListAttributesResponse__listOfVariable__Member*)
@ -740,8 +747,8 @@ mmsServer_handleGetNamedVariableListAttributesRequest(
goto exit_function;
}
if (request->present == ObjectName_PR_domainspecific) {
if (request->present == ObjectName_PR_domainspecific)
{
char domainName[65];
char itemName[65];
@ -761,11 +768,12 @@ mmsServer_handleGetNamedVariableListAttributesRequest(
MmsDomain* domain = MmsDevice_getDomain(mmsDevice, domainName);
if (domain != NULL) {
if (domain != NULL)
{
MmsNamedVariableList varList = MmsDomain_getNamedVariableList(domain, itemName);
if (varList) {
if (varList)
{
MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_GET_DIRECTORY, MMS_DOMAIN_SPECIFIC, domain, varList->name, connection);
if (accessError == MMS_ERROR_NONE) {
@ -792,8 +800,8 @@ mmsServer_handleGetNamedVariableListAttributesRequest(
}
#if (MMS_DYNAMIC_DATA_SETS == 1)
else if (request->present == ObjectName_PR_aaspecific) {
else if (request->present == ObjectName_PR_aaspecific)
{
char listName[65];
if (request->choice.aaspecific.size > 64) {
@ -806,11 +814,12 @@ mmsServer_handleGetNamedVariableListAttributesRequest(
MmsNamedVariableList varList = MmsServerConnection_getNamedVariableList(connection, listName);
if (varList) {
if (varList)
{
MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_GET_DIRECTORY, MMS_ASSOCIATION_SPECIFIC, NULL, varList->name, connection);
if (accessError == MMS_ERROR_NONE) {
if (accessError == MMS_ERROR_NONE)
{
if (createGetNamedVariableListAttributesResponse(invokeId, response, varList) == false) {
/* encoding failed - probably because buffer size is too small for message */
@ -829,7 +838,8 @@ mmsServer_handleGetNamedVariableListAttributesRequest(
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
}
#endif /* (MMS_DYNAMIC_DATA_SETS == 1) */
else if (request->present == ObjectName_PR_vmdspecific) {
else if (request->present == ObjectName_PR_vmdspecific)
{
char listName[65];
if (request->choice.vmdspecific.size > 64) {
@ -844,11 +854,12 @@ mmsServer_handleGetNamedVariableListAttributesRequest(
MmsNamedVariableList varList = mmsServer_getNamedVariableListWithName(mmsDevice->namedVariableLists, listName);
if (varList) {
if (varList)
{
MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_GET_DIRECTORY, MMS_VMD_SPECIFIC, NULL, varList->name, connection);
if (accessError == MMS_ERROR_NONE) {
if (accessError == MMS_ERROR_NONE)
{
if (createGetNamedVariableListAttributesResponse(invokeId, response, varList) == false) {
/* encoding failed - probably because buffer size is too small for message */
@ -870,6 +881,12 @@ mmsServer_handleGetNamedVariableListAttributesRequest(
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED);
}
if (ByteBuffer_getSize(response) > connection->maxPduSize)
{
ByteBuffer_setSize(response, 0);
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_OTHER);
}
exit_function:
asn_DEF_GetVariableAccessAttributesRequest.free_struct(&asn_DEF_GetNamedVariableListAttributesRequest,

@ -1,7 +1,7 @@
/*
* mms_read_service.c
*
* Copyright 2013-2023 Michael Zillgith
* Copyright 2013-2024 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -202,30 +202,35 @@ alternateArrayAccess(MmsServerConnection connection,
MmsValue* arrayValue = mmsServer_getValue(connection->server, domain, itemId, connection, false);
if (arrayValue != NULL) {
if (arrayValue != NULL)
{
MmsValue* value = NULL;
if (numberOfElements == 0)
if (mmsServer_isAccessToArrayComponent(alternateAccess)) {
{
if (mmsServer_isAccessToArrayComponent(alternateAccess))
{
if (namedVariable->typeSpec.array.elementTypeSpec->type == MMS_STRUCTURE) {
MmsValue* structValue = MmsValue_getElement(arrayValue, index);
if (structValue != NULL)
value = mmsServer_getComponentOfArrayElement(alternateAccess,
namedVariable, structValue);
namedVariable, structValue, NULL);
}
}
else {
value = MmsValue_getElement(arrayValue, index);
}
else {
}
else
{
value = MmsValue_createEmptyArray(numberOfElements);
MmsValue_setDeletable(value);
int resultIndex = 0;
while (index < lowIndex + numberOfElements) {
while (index < lowIndex + numberOfElements)
{
MmsValue* elementValue = NULL;
elementValue = MmsValue_getElement(arrayValue, index);
@ -352,7 +357,8 @@ encodeVariableAccessSpecification(VarAccessSpec* accessSpec, uint8_t* buffer, in
varAccessSpecSize += itemIdLen + BerEncoder_determineLengthSize(itemIdLen) + 1;
if (accessSpec->domainId != NULL) {
if (accessSpec->domainId != NULL)
{
uint32_t domainIdLen = strlen(accessSpec->domainId);
varAccessSpecSize += domainIdLen + BerEncoder_determineLengthSize(domainIdLen) + 1;
@ -370,7 +376,8 @@ encodeVariableAccessSpecification(VarAccessSpec* accessSpec, uint8_t* buffer, in
varAccessSpecSize += 1 + BerEncoder_determineLengthSize(varAccessSpecLength);
if (encode == false) {
if (encode == false)
{
bufPos = varAccessSpecSize;
goto exit_function;
}
@ -378,8 +385,8 @@ encodeVariableAccessSpecification(VarAccessSpec* accessSpec, uint8_t* buffer, in
/* encode to buffer */
bufPos = BerEncoder_encodeTL(0xa0, varAccessSpecLength, buffer, bufPos);
if (accessSpec->isNamedVariableList == true) {
if (accessSpec->isNamedVariableList == true)
{
bufPos = BerEncoder_encodeTL(0xa1, variableListNameLength, buffer, bufPos);
if (accessSpec->specific == 0) { /* vmd-specific */
@ -425,8 +432,8 @@ encodeReadResponse(MmsServerConnection connection,
/* iterate values list to determine encoded size */
LinkedList value = LinkedList_getNext(values);
for (i = 0; i < variableCount; i++) {
for (i = 0; i < variableCount; i++)
{
MmsValue* data = (MmsValue*) value->data;
accessResultSize += MmsValue_encodeMmsData(data, NULL, 0, false);
@ -452,12 +459,13 @@ encodeReadResponse(MmsServerConnection connection,
confirmedResponseContentSize;
/* Check if message would fit in the MMS PDU */
if (mmsPduSize > connection->maxPduSize) {
if (mmsPduSize > connection->maxPduSize)
{
if (DEBUG_MMS_SERVER)
printf("MMS read: message to large! send error PDU!\n");
mmsMsg_createServiceErrorPdu(invokeId, response,
MMS_ERROR_SERVICE_OTHER);
MMS_ERROR_RESOURCE_OTHER);
goto exit_function;
}
@ -487,7 +495,8 @@ encodeReadResponse(MmsServerConnection connection,
/* encode access results */
value = LinkedList_getNext(values);
for (i = 0; i < variableCount; i++) {
for (i = 0; i < variableCount; i++)
{
MmsValue* data = (MmsValue*) value->data;
bufPos = MmsValue_encodeMmsData(data, buffer, bufPos, true);
@ -529,16 +538,18 @@ handleReadListOfVariablesRequest(
int i;
for (i = 0; i < variableCount; i++) {
for (i = 0; i < variableCount; i++)
{
VariableSpecification_t varSpec =
read->variableAccessSpecification.choice.listOfVariable.list.array[i]->variableSpecification;
AlternateAccess_t* alternateAccess =
read->variableAccessSpecification.choice.listOfVariable.list.array[i]->alternateAccess;
if (varSpec.present == VariableSpecification_PR_name) {
if (varSpec.choice.name.present == ObjectName_PR_domainspecific) {
if (varSpec.present == VariableSpecification_PR_name)
{
if (varSpec.choice.name.present == ObjectName_PR_domainspecific)
{
char domainIdStr[65];
char nameIdStr[65];
@ -571,7 +582,8 @@ handleReadListOfVariablesRequest(
}
#if (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1)
else if (varSpec.choice.name.present == ObjectName_PR_vmdspecific) {
else if (varSpec.choice.name.present == ObjectName_PR_vmdspecific)
{
char nameIdStr[65];
mmsMsg_copyAsn1IdentifierToStringBuffer(varSpec.choice.name.choice.vmdspecific, nameIdStr, 65);
@ -607,11 +619,12 @@ handleReadListOfVariablesRequest(
LinkedList valueElement = LinkedList_getNext(values);
while (valueElement) {
while (valueElement)
{
MmsValue* value = (MmsValue*) LinkedList_getData(valueElement);
if (value) {
if (value)
{
if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) {
if (MmsValue_getDataAccessError(value) == DATA_ACCESS_ERROR_NO_RESPONSE) {
sendResponse = false;
@ -637,21 +650,24 @@ static void
addNamedVariableToNamedVariableListResultList(MmsVariableSpecification* namedVariable, MmsDomain* domain, char* nameIdStr,
LinkedList /*<MmsValue>*/ values, MmsServerConnection connection, MmsNamedVariableListEntry listEntry)
{
if (namedVariable != NULL) {
if (namedVariable != NULL)
{
if (DEBUG_MMS_SERVER)
printf("MMS read: found named variable %s with search string %s\n",
namedVariable->name, nameIdStr);
MmsValue* value = mmsServer_getValue(connection->server, domain, nameIdStr, connection, false);
if (value) {
if (listEntry->arrayIndex != -1) {
if (MmsValue_getType(value) == MMS_ARRAY) {
if (value)
{
if (listEntry->arrayIndex != -1)
{
if (MmsValue_getType(value) == MMS_ARRAY)
{
MmsValue* elementValue = MmsValue_getElement(value, listEntry->arrayIndex);
if (listEntry->componentName) {
if (listEntry->componentName)
{
MmsVariableSpecification* elementType = namedVariable->typeSpec.array.elementTypeSpec;
MmsValue* subElementValue = MmsVariableSpecification_getChildValue(elementType, elementValue, listEntry->componentName);
@ -669,7 +685,8 @@ addNamedVariableToNamedVariableListResultList(MmsVariableSpecification* namedVar
}
}
else {
else
{
if (DEBUG_MMS_SERVER)
printf("MMS_SERVER: data set entry of unexpected type!\n");
@ -697,8 +714,8 @@ createNamedVariableListResponse(MmsServerConnection connection, MmsNamedVariable
LinkedList variable = LinkedList_getNext(variables);
while (variable) {
while (variable)
{
MmsNamedVariableListEntry variableListEntry = (MmsNamedVariableListEntry) variable->data;
MmsDomain* variableDomain = MmsNamedVariableListEntry_getDomain(variableListEntry);
@ -757,11 +774,13 @@ handleReadNamedVariableListRequest(
MmsDomain* domain = MmsDevice_getDomain(MmsServer_getDevice(connection->server), domainIdStr);
if (domain == NULL) {
if (domain == NULL)
{
if (DEBUG_MMS_SERVER) printf("MMS read: domain %s not found!\n", domainIdStr);
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
}
else {
else
{
MmsNamedVariableList namedList = MmsDomain_getNamedVariableList(domain, nameIdStr);
if (namedList)
@ -796,12 +815,15 @@ handleReadNamedVariableListRequest(
MmsNamedVariableList namedList = mmsServer_getNamedVariableListWithName(connection->server->device->namedVariableLists, listName);
if (namedList == NULL)
{
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
else {
}
else
{
MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_READ, MMS_VMD_SPECIFIC, NULL, namedList->name, connection);
if (accessError == MMS_ERROR_NONE) {
if (accessError == MMS_ERROR_NONE)
{
VarAccessSpec accessSpec;
accessSpec.isNamedVariableList = true;
@ -816,7 +838,6 @@ handleReadNamedVariableListRequest(
mmsMsg_createServiceErrorPdu(invokeId, response, accessError);
}
}
}
#if (MMS_DYNAMIC_DATA_SETS == 1)
@ -831,13 +852,15 @@ handleReadNamedVariableListRequest(
MmsNamedVariableList namedList = MmsServerConnection_getNamedVariableList(connection, listName);
if (namedList == NULL)
{
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
else {
}
else
{
MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_READ, MMS_ASSOCIATION_SPECIFIC, NULL, namedList->name, connection);
if (accessError == MMS_ERROR_NONE) {
if (accessError == MMS_ERROR_NONE)
{
VarAccessSpec accessSpec;
accessSpec.isNamedVariableList = true;
@ -891,7 +914,8 @@ mmsServer_handleReadRequest(
goto exit_function;
}
if (request->variableAccessSpecification.present == VariableAccessSpecification_PR_listOfVariable) {
if (request->variableAccessSpecification.present == VariableAccessSpecification_PR_listOfVariable)
{
MmsServer_lockModel(connection->server);
handleReadListOfVariablesRequest(connection, request, invokeId, response);
@ -899,7 +923,8 @@ mmsServer_handleReadRequest(
MmsServer_unlockModel(connection->server);
}
#if (MMS_DATA_SET_SERVICE == 1)
else if (request->variableAccessSpecification.present == VariableAccessSpecification_PR_variableListName) {
else if (request->variableAccessSpecification.present == VariableAccessSpecification_PR_variableListName)
{
MmsServer_lockModel(connection->server);
handleReadNamedVariableListRequest(connection, request, invokeId, response);
@ -911,6 +936,12 @@ mmsServer_handleReadRequest(
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED);
}
if (ByteBuffer_getSize(response) > connection->maxPduSize)
{
ByteBuffer_setSize(response, 0);
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_OTHER);
}
exit_function:
asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0);
}

@ -536,9 +536,10 @@ mmsServer_setValue(MmsServer self, MmsDomain* domain, char* itemId, MmsValue* va
{
MmsDataAccessError indication;
if (self->writeHandler != NULL) {
if (self->writeHandler)
{
indication = self->writeHandler(self->writeHandlerParameter, domain,
itemId, value, connection);
itemId, -1, NULL, value, connection);
}
else {
MmsValue* cachedValue;
@ -548,7 +549,36 @@ mmsServer_setValue(MmsServer self, MmsDomain* domain, char* itemId, MmsValue* va
cachedValue = MmsServer_getValueFromCache(self, domain, itemId);
if (cachedValue != NULL) {
if (cachedValue) {
MmsValue_update(cachedValue, value);
indication = DATA_ACCESS_ERROR_SUCCESS;
} else
indication = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID;
}
return indication;
}
MmsDataAccessError
mmsServer_setValueEx(MmsServer self, MmsDomain* domain, char* itemId, MmsValue* value,
MmsServerConnection connection, int arrayIdx, const char* componentId)
{
MmsDataAccessError indication;
if (self->writeHandler)
{
indication = self->writeHandler(self->writeHandlerParameter, domain,
itemId, arrayIdx, componentId, value, connection);
}
else {
MmsValue* cachedValue = NULL;
if (domain == NULL)
domain = (MmsDomain*) self->device;
cachedValue = MmsServer_getValueFromCacheEx2(self, domain, itemId, arrayIdx, componentId);
if (cachedValue) {
MmsValue_update(cachedValue, value);
indication = DATA_ACCESS_ERROR_SUCCESS;
} else
@ -591,8 +621,6 @@ mmsServer_checkReadAccess(MmsServer self, MmsDomain* domain, char* itemId, MmsSe
{
MmsDataAccessError accessError = DATA_ACCESS_ERROR_SUCCESS;
printf("mmsServer_checkReadAccess(%s/%s)\n", domain->domainName, itemId);
if (self->readAccessHandler) {
accessError =
self->readAccessHandler(self->readAccessHandlerParameter, (domain == (MmsDomain*) self->device) ? NULL : domain,

@ -287,7 +287,7 @@ mmsServer_isAccessToArrayComponent(AlternateAccess_t* alternateAccess)
MmsValue*
mmsServer_getComponentOfArrayElement(AlternateAccess_t* alternateAccess, MmsVariableSpecification* namedVariable,
MmsValue* structuredValue)
MmsValue* structuredValue, char* componentId)
{
MmsValue* retValue = NULL;
@ -309,24 +309,43 @@ mmsServer_getComponentOfArrayElement(AlternateAccess_t* alternateAccess, MmsVari
goto exit_function;
int i;
for (i = 0; i < structSpec->typeSpec.structure.elementCount; i++) {
for (i = 0; i < structSpec->typeSpec.structure.elementCount; i++)
{
if ((int) strlen(structSpec->typeSpec.structure.elements[i]->name)
== component.size) {
== component.size)
{
if (strncmp(structSpec->typeSpec.structure.elements[i]->name,
(char*) component.buf, component.size) == 0) {
(char*) component.buf, component.size) == 0)
{
MmsValue* value = MmsValue_getElement(structuredValue, i);
if (mmsServer_isAccessToArrayComponent(
alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess)) {
retValue =
mmsServer_getComponentOfArrayElement(
alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess,
structSpec->typeSpec.structure.elements[i],
value);
if (value)
{
if (mmsServer_isAccessToArrayComponent(
alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess))
{
if (componentId)
{
strcat(componentId, structSpec->typeSpec.structure.elements[i]->name);
strcat(componentId, "$");
}
retValue =
mmsServer_getComponentOfArrayElement(
alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess,
structSpec->typeSpec.structure.elements[i],
value, componentId);
}
else
{
if (componentId)
{
strcat(componentId, structSpec->typeSpec.structure.elements[i]->name);
}
retValue = value;
}
}
else
retValue = value;
goto exit_function;
}

@ -517,33 +517,36 @@ getComponent(MmsServerConnection connection, MmsDomain* domain, AlternateAccess_
{
MmsVariableSpecification* retValue = NULL;
if (mmsServer_isComponentAccess(alternateAccess)) {
if (mmsServer_isComponentAccess(alternateAccess))
{
Identifier_t component =
alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.component;
if (component.size > 129)
goto exit_function;
if (namedVariable->type == MMS_STRUCTURE) {
if (namedVariable->type == MMS_STRUCTURE)
{
int i;
for (i = 0; i < namedVariable->typeSpec.structure.elementCount; i++) {
for (i = 0; i < namedVariable->typeSpec.structure.elementCount; i++)
{
if ((int) strlen(namedVariable->typeSpec.structure.elements[i]->name)
== component.size) {
== component.size)
{
if (!strncmp(namedVariable->typeSpec.structure.elements[i]->name,
(char*) component.buf, component.size))
{
if (strlen(variableName) + component.size < 199) {
if (strlen(variableName) + component.size < 199)
{
StringUtils_appendString(variableName, 200, "$");
/* here we need strncat because component.buf is not null terminated! */
strncat(variableName, (const char*)component.buf, (size_t)component.size);
if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess
!= NULL) {
!= NULL)
{
retValue =
getComponent(connection, domain,
alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess,
@ -598,12 +601,13 @@ mmsServer_handleWriteRequest(
MmsServer_lockModel(connection->server);
if (writeRequest->variableAccessSpecification.present == VariableAccessSpecification_PR_variableListName) {
if (writeRequest->variableAccessSpecification.present == VariableAccessSpecification_PR_variableListName)
{
handleWriteNamedVariableListRequest(connection, writeRequest, invokeId, response);
goto exit_function;
}
else if (writeRequest->variableAccessSpecification.present == VariableAccessSpecification_PR_listOfVariable) {
else if (writeRequest->variableAccessSpecification.present == VariableAccessSpecification_PR_listOfVariable)
{
int numberOfWriteItems = writeRequest->variableAccessSpecification.choice.listOfVariable.list.count;
if (numberOfWriteItems < 1) {
@ -627,7 +631,8 @@ mmsServer_handleWriteRequest(
int i;
for (i = 0; i < numberOfWriteItems; i++) {
for (i = 0; i < numberOfWriteItems; i++)
{
ListOfVariableSeq_t* varSpec =
writeRequest->variableAccessSpecification.choice.listOfVariable.list.array[i];
@ -644,7 +649,8 @@ mmsServer_handleWriteRequest(
char nameIdStr[65];
if (varSpec->variableSpecification.choice.name.present == ObjectName_PR_domainspecific) {
if (varSpec->variableSpecification.choice.name.present == ObjectName_PR_domainspecific)
{
Identifier_t domainId = varSpec->variableSpecification.choice.name.choice.domainspecific.domainId;
char domainIdStr[65];
@ -687,8 +693,8 @@ mmsServer_handleWriteRequest(
AlternateAccess_t* alternateAccess = varSpec->alternateAccess;
if (alternateAccess != NULL) {
if (alternateAccess)
{
if ((variable->type == MMS_STRUCTURE) && (mmsServer_isComponentAccess(alternateAccess) == false)) {
accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
continue;
@ -714,12 +720,13 @@ mmsServer_handleWriteRequest(
continue;
}
if (alternateAccess != NULL) {
if (alternateAccess)
{
if (domain == NULL)
domain = (MmsDomain*) device;
if (mmsServer_isIndexAccess(alternateAccess)) {
if (mmsServer_isIndexAccess(alternateAccess))
{
MmsValue* cachedArray = MmsServer_getValueFromCache(connection->server, domain, nameIdStr);
if (cachedArray == NULL) {
@ -730,8 +737,8 @@ mmsServer_handleWriteRequest(
int index = mmsServer_getLowIndex(alternateAccess);
int numberOfElements = mmsServer_getNumberOfElements(alternateAccess);
if (numberOfElements == 0) { /* select single array element with index */
if (numberOfElements == 0) /* select single array element with index */
{
MmsValue* elementValue = MmsValue_getElement(cachedArray, index);
if (elementValue == NULL) {
@ -739,34 +746,45 @@ mmsServer_handleWriteRequest(
goto end_of_main_loop;
}
if (mmsServer_isAccessToArrayComponent(alternateAccess)) {
if (mmsServer_isAccessToArrayComponent(alternateAccess))
{
MmsVariableSpecification* namedVariable = MmsDomain_getNamedVariable(domain, nameIdStr);
char componentId[65];
componentId[0] = 0;
if (namedVariable) {
elementValue = mmsServer_getComponentOfArrayElement(alternateAccess, namedVariable, elementValue);
elementValue = mmsServer_getComponentOfArrayElement(alternateAccess, namedVariable, elementValue, componentId);
}
if ((namedVariable == NULL) || (elementValue == NULL)) {
accessResults[i] = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT;
goto end_of_main_loop;
}
}
else
{
accessResults[i] = mmsServer_setValueEx(connection->server, domain, nameIdStr, value, connection, index, componentId);
}
if (MmsValue_update(elementValue, value) == false) {
accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
goto end_of_main_loop;
}
else
{
accessResults[i] = mmsServer_setValueEx(connection->server, domain, nameIdStr, value, connection, index, NULL);
goto end_of_main_loop;
}
}
else { /* select sub-array with start-index and number-of-elements */
if (MmsValue_getType(value) != MMS_ARRAY) {
else /* select sub-array with start-index and number-of-elements */
{
if (MmsValue_getType(value) != MMS_ARRAY)
{
accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
goto end_of_main_loop;
}
int elementNo;
for (elementNo = 0; elementNo < numberOfElements; elementNo++) {
for (elementNo = 0; elementNo < numberOfElements; elementNo++)
{
MmsValue* newElement = MmsValue_getElement(value, elementNo);
MmsValue* elementValue = MmsValue_getElement(cachedArray, index++);
@ -785,36 +803,37 @@ mmsServer_handleWriteRequest(
accessResults[i] = DATA_ACCESS_ERROR_SUCCESS;
goto end_of_main_loop;
}
else if (mmsServer_isComponentAccess(alternateAccess)) {
else if (mmsServer_isComponentAccess(alternateAccess))
{
variable = getComponent(connection, domain, alternateAccess, variable, nameIdStr);
if (variable == NULL) {
if (variable == NULL)
{
accessResults[i] = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT;
goto end_of_main_loop;
}
}
else {
else
{
accessResults[i] = DATA_ACCESS_ERROR_SUCCESS;
goto end_of_main_loop;
}
}
/* Check for correct type */
if (MmsVariableSpecification_isValueOfType(variable, value) == false) {
if (MmsVariableSpecification_isValueOfType(variable, value) == false)
{
accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
goto end_of_main_loop;
}
MmsDataAccessError valueIndication =
mmsServer_setValue(connection->server, domain, nameIdStr, value, connection);
if (valueIndication == DATA_ACCESS_ERROR_NO_RESPONSE)
sendResponse = false;
accessResults[i] = valueIndication;
accessResults[i] = mmsServer_setValue(connection->server, domain, nameIdStr, value, connection);
end_of_main_loop:
if (accessResults[i] == DATA_ACCESS_ERROR_NO_RESPONSE)
sendResponse = false;
MmsValue_delete(value);
}

@ -4,7 +4,7 @@ mkdir build
find src/ -name "*.java" > listFile.tmp
javac -target 1.6 -source 1.6 -d build @listFile.tmp
javac -target 1.8 -source 1.8 -d build @listFile.tmp
jar cfm genconfig.jar manifest-dynamic.mf -C build/ com/

@ -188,19 +188,35 @@ public class DataModelValue {
case TIMESTAMP:
case ENTRY_TIME:
try {
{
String modValueString = value.replace(',', '.');
SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-d'T'HH:mm:ss.SSS");
parser.setTimeZone(TimeZone.getTimeZone("UTC"));
try {
SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-d'T'HH:mm:ss.SSS");
parser.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = parser.parse(modValueString);
Date date = parser.parse(modValueString);
this.value = new Long(date.toInstant().toEpochMilli());
break;
}
catch (java.text.ParseException e) {};
try {
SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-d'T'HH:mm:ss");
parser.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = parser.parse(modValueString);
this.value = new Long(date.toInstant().toEpochMilli());
}
catch (java.text.ParseException e) {
this.value = new Long(date.toInstant().toEpochMilli());
break;
}
catch (java.text.ParseException e) {};
this.value = null;
System.out.println("Warning: Val element does not contain a valid time stamp: " + e.getMessage());
System.out.println("Warning: Val element does not contain a valid time stamp: " + value);
}
break;

@ -3,7 +3,7 @@ package com.libiec61850.tools;
/*
* DynamicModelGenerator.java
*
* Copyright 2014-2020 Michael Zillgith
* Copyright 2014-2024 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -447,11 +447,7 @@ public class DynamicModelGenerator {
output.println("}");
}
private void exportDataObject(PrintStream output, DataObject dataObject, boolean isTransient) {
if (dataObject.isTransient())
isTransient = true;
private void exportDataObjectChild(PrintStream output, DataObject dataObject, boolean isTransient) {
for (DataObject subDataObject : dataObject.getSubDataObjects()) {
output.print("DO(" + subDataObject.getName() + " " + subDataObject.getCount() + "){\n");
@ -465,6 +461,105 @@ public class DynamicModelGenerator {
}
}
private void exportDataObject(PrintStream output, DataObject dataObject, boolean isTransient) {
if (dataObject.isTransient())
isTransient = true;
if (dataObject.getCount() > 0) {
/* data object is an array */
for (int i = 0; i < dataObject.getCount(); i++) {
output.print("[" + i + "]{\n");
exportDataObjectChild(output, dataObject, isTransient);
output.print("}\n");
}
}
else {
exportDataObjectChild(output, dataObject, isTransient);
}
}
private void printDataAttributeValue(PrintStream output, DataAttribute dataAttribute, boolean isTransient)
{
if (dataAttribute.isBasicAttribute()) {
DataModelValue value = dataAttribute.getValue();
/* if no value is given use default value for type if present */
if (value == null) {
value = dataAttribute.getDefinition().getValue();
if (value != null)
if (value.getValue() == null)
value.updateEnumOrdValue(ied.getTypeDeclarations());
}
if (value != null) {
switch (dataAttribute.getType()) {
case ENUMERATED:
case INT8:
case INT16:
case INT32:
case INT64:
output.print("=" + value.getIntValue());
break;
case INT8U:
case INT16U:
case INT24U:
case INT32U:
output.print("=" + value.getLongValue());
break;
case BOOLEAN:
{
Boolean boolVal = (Boolean) value.getValue();
if (boolVal.booleanValue())
output.print("=1");
}
break;
case UNICODE_STRING_255:
output.print("=\"" + value.getValue()+ "\"");
break;
case CURRENCY:
case VISIBLE_STRING_32:
case VISIBLE_STRING_64:
case VISIBLE_STRING_129:
case VISIBLE_STRING_255:
case VISIBLE_STRING_65:
output.print("=\"" + value.getValue()+ "\"");
break;
case FLOAT32:
case FLOAT64:
output.print("=" + value.getValue());
break;
case TIMESTAMP:
case ENTRY_TIME:
output.print("=" + value.getLongValue());
break;
default:
System.out.println("Unknown default value for " + dataAttribute.getName() + " type: " + dataAttribute.getType());
break;
}
}
output.println(";");
}
else {
output.println("{");
for (DataAttribute subDataAttribute : dataAttribute.getSubDataAttributes()) {
exportDataAttribute(output, subDataAttribute, isTransient);
}
output.println("}");
}
}
private void exportDataAttribute(PrintStream output, DataAttribute dataAttribute, boolean isTransient) {
output.print("DA(" + dataAttribute.getName() + " ");
@ -493,78 +588,22 @@ public class DynamicModelGenerator {
else
output.print("0");
output.print(")");
if (dataAttribute.isBasicAttribute()) {
DataModelValue value = dataAttribute.getValue();
/* if no value is given use default value for type if present */
if (value == null) {
value = dataAttribute.getDefinition().getValue();
if (value != null)
if (value.getValue() == null)
value.updateEnumOrdValue(ied.getTypeDeclarations());
}
if (value != null) {
switch (dataAttribute.getType()) {
case ENUMERATED:
case INT8:
case INT16:
case INT32:
case INT64:
output.print("=" + value.getIntValue());
break;
case INT8U:
case INT16U:
case INT24U:
case INT32U:
output.print("=" + value.getLongValue());
break;
case BOOLEAN:
{
Boolean boolVal = (Boolean) value.getValue();
if (boolVal.booleanValue())
output.print("=1");
}
break;
case UNICODE_STRING_255:
output.print("=\"" + value.getValue()+ "\"");
break;
case CURRENCY:
case VISIBLE_STRING_32:
case VISIBLE_STRING_64:
case VISIBLE_STRING_129:
case VISIBLE_STRING_255:
case VISIBLE_STRING_65:
output.print("=\"" + value.getValue()+ "\"");
break;
case FLOAT32:
case FLOAT64:
output.print("=" + value.getValue());
break;
default:
System.out.println("Unknown default value for " + dataAttribute.getName() + " type: " + dataAttribute.getType());
break;
}
}
output.println(";");
}
else {
output.println("{");
output.print(")");
for (DataAttribute subDataAttribute : dataAttribute.getSubDataAttributes()) {
exportDataAttribute(output, subDataAttribute, isTransient);
if (dataAttribute.getCount() > 0) {
output.print("{\n");
for (int i = 0; i < dataAttribute.getCount(); i++) {
output.print("[" + i + "]");
printDataAttributeValue(output, dataAttribute, isTransient);
}
output.println("}");
output.print("}\n");
}
else {
printDataAttributeValue(output, dataAttribute, isTransient);
}
}
public static void main(String[] args) throws FileNotFoundException {

Loading…
Cancel
Save