diff --git a/dotnet/IEC61850forCSharp/Control.cs b/dotnet/IEC61850forCSharp/Control.cs
index b3c2bcc9..97a3d767 100644
--- a/dotnet/IEC61850forCSharp/Control.cs
+++ b/dotnet/IEC61850forCSharp/Control.cs
@@ -775,7 +775,10 @@ namespace IEC61850
Dispose (true);
}
-
+ ~ControlObject()
+ {
+ Dispose (false);
+ }
}
}
diff --git a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs
index 4b45fe54..16d79a56 100644
--- a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs
+++ b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs
@@ -1,7 +1,7 @@
/*
* IEC61850ClientAPI.cs
*
- * Copyright 2014-2021 Michael Zillgith
+ * Copyright 2014-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -437,6 +437,9 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_connect(IntPtr self, out int error, string hostname, int tcpPort);
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedConnection_setLocalAddress(IntPtr self, string localIpAddress, int localPort);
+
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_abort(IntPtr self, out int error);
@@ -894,6 +897,25 @@ namespace IEC61850
Connect(hostname, -1);
}
+ ///
+ /// Set the local IP address and port to be used by the client
+ ///
+ /// the local IP address or hostname
+ /// the local TCP port to use. When 0 the OS will chose the TCP port to use.
+ public void SetLocalAddress(string localIpAddress, int localPort)
+ {
+ IedConnection_setLocalAddress(connection, localIpAddress, localPort);
+ }
+
+ ///
+ /// Set the local IP address to be used by the client
+ ///
+ /// the local IP address or hostname
+ public void SetLocalAddress(string localIpAddress)
+ {
+ IedConnection_setLocalAddress(connection, localIpAddress, 0);
+ }
+
/// This exception is thrown if there is a connection or service error
public ControlObject CreateControlObject(string objectReference)
{
diff --git a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs
index c5c821cc..6e52a259 100644
--- a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs
+++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs
@@ -1836,6 +1836,14 @@ namespace IEC61850
[return: MarshalAs(UnmanagedType.I1)]
static extern bool ControlAction_isSelect(IntPtr self);
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ static extern bool ControlAction_getSynchroCheck(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ static extern bool ControlAction_getInterlockCheck(IntPtr self);
+
private IntPtr self;
private IedServer.ControlHandlerInfo info;
private IedServer iedServer;
@@ -1955,6 +1963,16 @@ namespace IEC61850
{
return ControlAction_isSelect(self);
}
+
+ public bool GetSynchroCheck()
+ {
+ return ControlAction_getSynchroCheck(self);
+ }
+
+ public bool GetInterlockCheck()
+ {
+ return ControlAction_getInterlockCheck(self);
+ }
}
public delegate void GoCBEventHandler(MmsGooseControlBlock goCB, int cbEvent, object parameter);
@@ -2408,6 +2426,9 @@ namespace IEC61850
/* store IedModel instance to prevent garbage collector */
private IedModel iedModel = null;
+ /* store TLSConfiguration instance to prevent garbage collector */
+ private TLSConfiguration tlsConfiguration = null;
+
public IedServer(IedModel iedModel, IedServerConfig config = null)
{
this.iedModel = iedModel;
@@ -2423,6 +2444,7 @@ namespace IEC61850
public IedServer(IedModel iedModel, TLSConfiguration tlsConfig, IedServerConfig config = null)
{
this.iedModel = iedModel;
+ this.tlsConfiguration = tlsConfig;
IntPtr nativeConfig = IntPtr.Zero;
IntPtr nativeTLSConfig = IntPtr.Zero;
@@ -2512,6 +2534,7 @@ namespace IEC61850
self = IntPtr.Zero;
internalConnectionHandler = null;
this.iedModel = null;
+ this.tlsConfiguration = null;
}
}
}
diff --git a/examples/iec61850_client_example1/client_example1.c b/examples/iec61850_client_example1/client_example1.c
index f84d37a0..6c9241f0 100644
--- a/examples/iec61850_client_example1/client_example1.c
+++ b/examples/iec61850_client_example1/client_example1.c
@@ -33,6 +33,9 @@ int main(int argc, char** argv) {
char* hostname;
int tcpPort = 102;
+ const char* localIp = NULL;
+ int localTcpPort = -1;
+
if (argc > 1)
hostname = argv[1];
@@ -42,19 +45,34 @@ int main(int argc, char** argv) {
if (argc > 2)
tcpPort = atoi(argv[2]);
+ if (argc > 3)
+ localIp = argv[3];
+
+ if (argc > 4)
+ localTcpPort = atoi(argv[4]);
+
IedClientError error;
IedConnection con = IedConnection_create();
+ /* Optional bind to local IP address/interface */
+ if (localIp) {
+ IedConnection_setLocalAddress(con, localIp, localTcpPort);
+ printf("Bound to Local Address: %s:%i\n", localIp, localTcpPort);
+ }
+
IedConnection_connect(con, &error, hostname, tcpPort);
+ printf("Connecting to %s:%i\n", hostname, tcpPort);
- if (error == IED_ERROR_OK) {
+ if (error == IED_ERROR_OK)
+ {
+ printf("Connected\n");
/* read an analog measurement value from server */
MmsValue* value = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.AnIn1.mag.f", IEC61850_FC_MX);
- if (value != NULL) {
-
+ if (value != NULL)
+ {
if (MmsValue_getType(value) == MMS_FLOAT) {
float fval = MmsValue_toFloat(value);
printf("read float value: %f\n", fval);
@@ -137,7 +155,6 @@ close_connection:
}
IedConnection_destroy(con);
+
return 0;
}
-
-
diff --git a/examples/iec61850_client_example_control/client_example_control.c b/examples/iec61850_client_example_control/client_example_control.c
index 33da635a..37d6b6bc 100644
--- a/examples/iec61850_client_example_control/client_example_control.c
+++ b/examples/iec61850_client_example_control/client_example_control.c
@@ -45,8 +45,10 @@ int main(int argc, char** argv) {
IedConnection_connect(con, &error, hostname, tcpPort);
- if (error == IED_ERROR_OK) {
-
+ if (error == IED_ERROR_OK)
+ {
+ MmsValue* ctlVal = NULL;
+ MmsValue* stVal = NULL;
/************************
* Direct control
@@ -55,99 +57,116 @@ int main(int argc, char** argv) {
ControlObjectClient control
= ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO1", con);
- MmsValue* ctlVal = MmsValue_newBoolean(true);
+ if (control)
+ {
+ ctlVal = MmsValue_newBoolean(true);
- ControlObjectClient_setOrigin(control, NULL, 3);
+ ControlObjectClient_setOrigin(control, NULL, 3);
- if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
- printf("simpleIOGenericIO/GGIO1.SPCSO1 operated successfully\n");
- }
- else {
- printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO1\n");
- }
+ if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
+ printf("simpleIOGenericIO/GGIO1.SPCSO1 operated successfully\n");
+ }
+ else {
+ printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO1\n");
+ }
+
+ MmsValue_delete(ctlVal);
- MmsValue_delete(ctlVal);
+ ControlObjectClient_destroy(control);
- ControlObjectClient_destroy(control);
+ /* Check if status value has changed */
- /* Check if status value has changed */
+ stVal = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.SPCSO1.stVal", IEC61850_FC_ST);
- MmsValue* stVal = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.SPCSO1.stVal", IEC61850_FC_ST);
+ if (error == IED_ERROR_OK) {
+ bool state = MmsValue_getBoolean(stVal);
+ MmsValue_delete(stVal);
- if (error == IED_ERROR_OK) {
- bool state = MmsValue_getBoolean(stVal);
- MmsValue_delete(stVal);
+ printf("New status of simpleIOGenericIO/GGIO1.SPCSO1.stVal: %i\n", state);
+ }
+ else {
+ printf("Reading status for simpleIOGenericIO/GGIO1.SPCSO1 failed!\n");
+ }
- printf("New status of simpleIOGenericIO/GGIO1.SPCSO1.stVal: %i\n", state);
}
else {
- printf("Reading status for simpleIOGenericIO/GGIO1.SPCSO1 failed!\n");
+ printf("Control object simpleIOGenericIO/GGIO1.SPCSO1 not found in server\n");
}
-
/************************
* Select before operate
***********************/
control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO2", con);
- if (ControlObjectClient_select(control)) {
+ if (control)
+ {
+ if (ControlObjectClient_select(control)) {
- ctlVal = MmsValue_newBoolean(true);
+ ctlVal = MmsValue_newBoolean(true);
- if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
- printf("simpleIOGenericIO/GGIO1.SPCSO2 operated successfully\n");
+ if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
+ printf("simpleIOGenericIO/GGIO1.SPCSO2 operated successfully\n");
+ }
+ else {
+ printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO2!\n");
+ }
+
+ MmsValue_delete(ctlVal);
}
else {
- printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO2!\n");
+ printf("failed to select simpleIOGenericIO/GGIO1.SPCSO2!\n");
}
- MmsValue_delete(ctlVal);
+ ControlObjectClient_destroy(control);
}
else {
- printf("failed to select simpleIOGenericIO/GGIO1.SPCSO2!\n");
+ printf("Control object simpleIOGenericIO/GGIO1.SPCSO2 not found in server\n");
}
- ControlObjectClient_destroy(control);
-
-
/****************************************
* Direct control with enhanced security
****************************************/
control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO3", con);
- ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL);
+ if (control)
+ {
+ ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL);
- ctlVal = MmsValue_newBoolean(true);
+ ctlVal = MmsValue_newBoolean(true);
- if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
- printf("simpleIOGenericIO/GGIO1.SPCSO3 operated successfully\n");
- }
- else {
- printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO3\n");
- }
+ if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
+ printf("simpleIOGenericIO/GGIO1.SPCSO3 operated successfully\n");
+ }
+ else {
+ printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO3\n");
+ }
- MmsValue_delete(ctlVal);
+ MmsValue_delete(ctlVal);
- /* Wait for command termination message */
- Thread_sleep(1000);
+ /* Wait for command termination message */
+ Thread_sleep(1000);
- ControlObjectClient_destroy(control);
+ ControlObjectClient_destroy(control);
- /* Check if status value has changed */
+ /* Check if status value has changed */
- stVal = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.SPCSO3.stVal", IEC61850_FC_ST);
+ stVal = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.SPCSO3.stVal", IEC61850_FC_ST);
- if (error == IED_ERROR_OK) {
- bool state = MmsValue_getBoolean(stVal);
+ if (error == IED_ERROR_OK) {
+ bool state = MmsValue_getBoolean(stVal);
- printf("New status of simpleIOGenericIO/GGIO1.SPCSO3.stVal: %i\n", state);
+ printf("New status of simpleIOGenericIO/GGIO1.SPCSO3.stVal: %i\n", state);
- MmsValue_delete(stVal);
+ MmsValue_delete(stVal);
+ }
+ else {
+ printf("Reading status for simpleIOGenericIO/GGIO1.SPCSO3 failed!\n");
+ }
}
else {
- printf("Reading status for simpleIOGenericIO/GGIO1.SPCSO3 failed!\n");
+ printf("Control object simpleIOGenericIO/GGIO1.SPCSO3 not found in server\n");
}
/***********************************************
@@ -156,56 +175,66 @@ int main(int argc, char** argv) {
control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO4", con);
- ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL);
+ if (control)
+ {
+ ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL);
- ctlVal = MmsValue_newBoolean(true);
+ ctlVal = MmsValue_newBoolean(true);
- if (ControlObjectClient_selectWithValue(control, ctlVal)) {
+ if (ControlObjectClient_selectWithValue(control, ctlVal)) {
+
+ if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
+ printf("simpleIOGenericIO/GGIO1.SPCSO4 operated successfully\n");
+ }
+ else {
+ printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO4!\n");
+ }
- if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
- printf("simpleIOGenericIO/GGIO1.SPCSO4 operated successfully\n");
}
else {
- printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO4!\n");
+ printf("failed to select simpleIOGenericIO/GGIO1.SPCSO4!\n");
}
+ MmsValue_delete(ctlVal);
+
+ /* Wait for command termination message */
+ Thread_sleep(1000);
+
+ ControlObjectClient_destroy(control);
}
else {
- printf("failed to select simpleIOGenericIO/GGIO1.SPCSO4!\n");
+ printf("Control object simpleIOGenericIO/GGIO1.SPCSO4 not found in server\n");
}
- MmsValue_delete(ctlVal);
-
- /* Wait for command termination message */
- Thread_sleep(1000);
-
- ControlObjectClient_destroy(control);
-
-
/*********************************************************************
* Direct control with enhanced security (expect CommandTermination-)
*********************************************************************/
control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO9", con);
- ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL);
-
- ctlVal = MmsValue_newBoolean(true);
+ if (control)
+ {
+ ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL);
- if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
- printf("simpleIOGenericIO/GGIO1.SPCSO9 operated successfully\n");
- }
- else {
- printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO9\n");
- }
+ ctlVal = MmsValue_newBoolean(true);
- MmsValue_delete(ctlVal);
+ if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
+ printf("simpleIOGenericIO/GGIO1.SPCSO9 operated successfully\n");
+ }
+ else {
+ printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO9\n");
+ }
- /* Wait for command termination message */
- Thread_sleep(1000);
+ MmsValue_delete(ctlVal);
- ControlObjectClient_destroy(control);
+ /* Wait for command termination message */
+ Thread_sleep(1000);
+ ControlObjectClient_destroy(control);
+ }
+ else {
+ printf("Control object simpleIOGenericIO/GGIO1.SPCSO9 not found in server\n");
+ }
IedConnection_close(con);
}
diff --git a/examples/mms_utility/mms_utility.c b/examples/mms_utility/mms_utility.c
index 8b43d9d7..24c909c9 100644
--- a/examples/mms_utility/mms_utility.c
+++ b/examples/mms_utility/mms_utility.c
@@ -101,6 +101,7 @@ printRawMmsMessage(void* parameter, uint8_t* message, int messageLength, bool re
int main(int argc, char** argv)
{
+ int returnCode = 0;
char* hostname = StringUtils_copyString("localhost");
int tcpPort = 102;
@@ -213,6 +214,10 @@ int main(int argc, char** argv)
if (!MmsConnection_connect(con, &error, hostname, tcpPort)) {
printf("MMS connect failed!\n");
+
+ if (error != MMS_ERROR_NONE)
+ returnCode = error;
+
goto exit;
}
else
@@ -222,6 +227,9 @@ int main(int argc, char** argv)
MmsServerIdentity* identity =
MmsConnection_identify(con, &error);
+ if (error != MMS_ERROR_NONE)
+ returnCode = error;
+
if (identity != NULL) {
printf("\nServer identity:\n----------------\n");
printf(" vendor:\t%s\n", identity->vendorName);
@@ -235,14 +243,23 @@ int main(int argc, char** argv)
if (readDeviceList) {
printf("\nDomains present on server:\n--------------------------\n");
LinkedList nameList = MmsConnection_getDomainNames(con, &error);
- LinkedList_printStringList(nameList);
- LinkedList_destroy(nameList);
+
+ if (error != MMS_ERROR_NONE)
+ returnCode = error;
+
+ if (nameList) {
+ LinkedList_printStringList(nameList);
+ LinkedList_destroy(nameList);
+ }
}
if (getDeviceDirectory) {
LinkedList variableList = MmsConnection_getDomainVariableNames(con, &error,
domainName);
+ if (error != MMS_ERROR_NONE)
+ returnCode = error;
+
if (variableList) {
LinkedList element = LinkedList_getNext(variableList);
@@ -264,6 +281,9 @@ int main(int argc, char** argv)
variableList = MmsConnection_getDomainJournals(con, &error, domainName);
+ if (error != MMS_ERROR_NONE)
+ returnCode = error;
+
if (variableList) {
LinkedList element = variableList;
@@ -309,6 +329,9 @@ int main(int argc, char** argv)
LinkedList journalEntries = MmsConnection_readJournalTimeRange(con, &error, logDomain, logName, startTime, endTime,
&moreFollows);
+ if (error != MMS_ERROR_NONE)
+ returnCode = error;
+
MmsValue_delete(startTime);
MmsValue_delete(endTime);
@@ -375,6 +398,8 @@ int main(int argc, char** argv)
if (error != MMS_ERROR_NONE) {
printf("Reading variable failed: (ERROR %i)\n", error);
+
+ returnCode = error;
}
else {
printf("Read SUCCESS\n");
@@ -403,6 +428,8 @@ int main(int argc, char** argv)
if (error != MMS_ERROR_NONE) {
printf("Reading variable failed: (ERROR %i)\n", error);
+
+ returnCode = error;
}
else {
printf("Read SUCCESS\n");
@@ -421,6 +448,8 @@ int main(int argc, char** argv)
if (error != MMS_ERROR_NONE) {
printf("Reading variable list directory failed: (ERROR %i)\n", error);
+
+ returnCode = error;
}
else {
LinkedList varListElem = LinkedList_getNext(varListDir);
@@ -454,12 +483,19 @@ int main(int argc, char** argv)
char* continueAfter = NULL;
while (MmsConnection_getFileDirectory(con, &error, "", continueAfter, mmsFileDirectoryHandler, lastName)) {
+
+ if (error != MMS_ERROR_NONE)
+ returnCode = error;
+
continueAfter = lastName;
}
}
if (getFileAttributes) {
MmsConnection_getFileDirectory(con, &error, filename, NULL, mmsGetFileAttributeHandler, NULL);
+
+ if (error != MMS_ERROR_NONE)
+ returnCode = error;
}
if (deleteFile) {
@@ -467,13 +503,14 @@ int main(int argc, char** argv)
if (error != MMS_ERROR_NONE) {
printf("Delete file failed: (ERROR %i)\n", error);
+ returnCode = error;
}
else {
printf("File deleted\n");
}
}
- exit:
+exit:
free(hostname);
free(domainName);
free(variableName);
@@ -482,6 +519,6 @@ int main(int argc, char** argv)
MmsConnection_destroy(con);
- return 0;
+ return returnCode;
}
diff --git a/hal/ethernet/linux/ethernet_linux.c b/hal/ethernet/linux/ethernet_linux.c
index 2d97a826..eaf1897a 100644
--- a/hal/ethernet/linux/ethernet_linux.c
+++ b/hal/ethernet/linux/ethernet_linux.c
@@ -263,6 +263,8 @@ void
Ethernet_addMulticastAddress(EthernetSocket ethSocket, uint8_t* multicastAddress)
{
struct packet_mreq mreq;
+ memset(&mreq, 0, sizeof(struct packet_mreq));
+
mreq.mr_ifindex = ethSocket->socketAddress.sll_ifindex;
mreq.mr_alen = ETH_ALEN;
mreq.mr_type = PACKET_MR_MULTICAST;
diff --git a/src/common/string_utilities.c b/src/common/string_utilities.c
index dfb9d620..37e62ad7 100644
--- a/src/common/string_utilities.c
+++ b/src/common/string_utilities.c
@@ -194,6 +194,9 @@ StringUtils_copyStringMax(char* dest, int maxBufferSize, const char* str1)
{
char* res = dest;
+ if (maxBufferSize < 1)
+ return NULL;
+
if (dest == NULL)
res = (char*)GLOBAL_MALLOC(maxBufferSize);
diff --git a/src/goose/goose_receiver.c b/src/goose/goose_receiver.c
index 06160008..63fe9710 100644
--- a/src/goose/goose_receiver.c
+++ b/src/goose/goose_receiver.c
@@ -210,13 +210,22 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
case 0x84: /* BIT STRING */
if (MmsValue_getType(value) == MMS_BIT_STRING) {
int padding = buffer[bufPos];
- int bitStringLength = (8 * (elementLength - 1)) - padding;
- if (bitStringLength == value->value.bitString.size) {
- memcpy(value->value.bitString.buf, buffer + bufPos + 1,
- elementLength - 1);
+
+ if (padding > 7) {
+ if (DEBUG_GOOSE_SUBSCRIBER)
+ printf("GOOSE_SUBSCRIBER: invalid bit-string (padding not plausible)\n");
+
+ pe = GOOSE_PARSE_ERROR_INVALID_PADDING;
}
else {
- pe = GOOSE_PARSE_ERROR_LENGTH_MISMATCH;
+ int bitStringLength = (8 * (elementLength - 1)) - padding;
+ if (bitStringLength == value->value.bitString.size) {
+ memcpy(value->value.bitString.buf, buffer + bufPos + 1,
+ elementLength - 1);
+ }
+ else {
+ pe = GOOSE_PARSE_ERROR_LENGTH_MISMATCH;
+ }
}
}
else {
@@ -352,7 +361,7 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
break;
}
- if ( pe != GOOSE_PARSE_ERROR_NO_ERROR ) {
+ if (pe != GOOSE_PARSE_ERROR_NO_ERROR) {
break; /* from while */
}
@@ -362,14 +371,16 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
}
if (elementIndex <= maxIndex) {
- pe = GOOSE_PARSE_ERROR_UNDERFLOW;
+ if (pe == GOOSE_PARSE_ERROR_NO_ERROR) {
+ pe = GOOSE_PARSE_ERROR_UNDERFLOW;
+ }
}
if (DEBUG_GOOSE_SUBSCRIBER) {
- switch ( pe ) {
+ switch (pe) {
case GOOSE_PARSE_ERROR_UNKNOWN_TAG:
printf("GOOSE_SUBSCRIBER: Found unkown tag %02x!\n", tag);
- break;
+ break;
case GOOSE_PARSE_ERROR_TAGDECODE:
printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n");
break;
@@ -388,6 +399,8 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
case GOOSE_PARSE_ERROR_LENGTH_MISMATCH:
printf("GOOSE_SUBSCRIBER: Message contains value of wrong length!\n");
break;
+ case GOOSE_PARSE_ERROR_INVALID_PADDING:
+ printf("GOOSE_SUBSCRIBER: Malformed message: invalid padding!\n");
default:
break;
}
@@ -500,7 +513,16 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt
case 0x83: /* boolean */
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: found boolean\n");
- value = MmsValue_newBoolean(BerDecoder_decodeBoolean(buffer, bufPos));
+
+ if (elementLength > 0) {
+ value = MmsValue_newBoolean(BerDecoder_decodeBoolean(buffer, bufPos));
+ }
+ else {
+ if (DEBUG_GOOSE_SUBSCRIBER)
+ printf("GOOSE_SUBSCRIBER: invalid length for boolean\n");
+
+ goto exit_with_error;
+ }
break;
diff --git a/src/goose/goose_subscriber.h b/src/goose/goose_subscriber.h
index e58f4f96..7408d6b5 100644
--- a/src/goose/goose_subscriber.h
+++ b/src/goose/goose_subscriber.h
@@ -47,6 +47,7 @@ typedef enum
GOOSE_PARSE_ERROR_UNDERFLOW,
GOOSE_PARSE_ERROR_TYPE_MISMATCH,
GOOSE_PARSE_ERROR_LENGTH_MISMATCH,
+ GOOSE_PARSE_ERROR_INVALID_PADDING
} GooseParseError;
typedef struct sGooseSubscriber* GooseSubscriber;
diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c
index b49a1fe7..bc581dea 100644
--- a/src/iec61850/client/ied_connection.c
+++ b/src/iec61850/client/ied_connection.c
@@ -651,6 +651,15 @@ IedConnection_tick(IedConnection self)
return MmsConnection_tick(self->connection);
}
+void
+IedConnection_setLocalAddress(IedConnection self, const char* localIpAddress, int localPort)
+{
+ MmsConnection connection = self->connection;
+ IsoConnectionParameters isoP = MmsConnection_getIsoConnectionParameters(connection);
+
+ IsoConnectionParameters_setLocalTcpParameters(isoP, localIpAddress, localPort);
+}
+
void
IedConnection_setConnectTimeout(IedConnection self, uint32_t timeoutInMs)
{
diff --git a/src/iec61850/inc/iec61850_client.h b/src/iec61850/inc/iec61850_client.h
index 190c6bce..986c951c 100644
--- a/src/iec61850/inc/iec61850_client.h
+++ b/src/iec61850/inc/iec61850_client.h
@@ -231,6 +231,17 @@ IedConnection_createWithTlsSupport(TLSConfiguration tlsConfig);
LIB61850_API void
IedConnection_destroy(IedConnection self);
+/**
+* \brief Set the local IP address and port to be used by the client
+*
+* NOTE: This function is optional. When not used the OS decides what IP address and TCP port to use.
+*
+* \param self IedConnection instance
+* \param localIpAddress the local IP address or hostname as C string
+* \param localPort the local TCP port to use. When < 1 the OS will chose the TCP port to use.
+*/
+LIB61850_API void
+IedConnection_setLocalAddress(IedConnection self, const char* localIpAddress, int localPort);
/**
* \brief set the connect timeout in ms
diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h
index d206b904..2a7d6f2f 100644
--- a/src/iec61850/inc/iec61850_server.h
+++ b/src/iec61850/inc/iec61850_server.h
@@ -3,7 +3,7 @@
*
* IEC 61850 server API for libiec61850.
*
- * Copyright 2013-2022 Michael Zillgith
+ * Copyright 2013-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -42,6 +42,13 @@ extern "C" {
#include "iso_connection_parameters.h"
#include "iec61850_config_file_parser.h"
+#define IEC61850_REPORTSETTINGS_RPT_ID 1
+#define IEC61850_REPORTSETTINGS_BUF_TIME 2
+#define IEC61850_REPORTSETTINGS_DATSET 4
+#define IEC61850_REPORTSETTINGS_TRG_OPS 8
+#define IEC61850_REPORTSETTINGS_OPT_FIELDS 16
+#define IEC61850_REPORTSETTINGS_INTG_PD 32
+
/**
* \brief Configuration object to configure IEC 61850 stack features
*/
@@ -99,6 +106,9 @@ struct sIedServerConfig
/** integrity report start times will by synchronized with straight numbers (default: false) */
bool syncIntegrityReportTimes;
+
+ /** for each configurable ReportSetting there is a separate flag (default: Dyn = enable write for all) */
+ uint8_t reportSettingsWritable;
};
/**
@@ -388,6 +398,27 @@ IedServerConfig_useIntegratedGoosePublisher(IedServerConfig self, bool enable);
LIB61850_API bool
IedServerConfig_isLogServiceEnabled(IedServerConfig self);
+/**
+ * \brief Make a configurable report setting writeable or read-only
+ *
+ * \note Can be used to implement some of Services\ReportSettings options
+ *
+ * \param[in] setting one of IEC61850_REPORTSETTINGS_RPT_ID, _BUF_TIME, _DATSET, _TRG_OPS, _OPT_FIELDS, _INTG_PD
+ * \param[in] isDyn true, when setting is writable ("Dyn") or false, when read-only
+ */
+LIB61850_API void
+IedServerConfig_setReportSetting(IedServerConfig self, uint8_t setting, bool isDyn);
+
+/**
+ * \brief Check if a configurable report setting is writable or read-only
+ *
+ * \param[in] setting one of IEC61850_REPORTSETTINGS_RPT_ID, _BUF_TIME, _DATSET, _TRG_OPS, _OPT_FIELDS, _INTG_PD
+ *
+ * \return isDyn true, when setting is writable ("Dyn") or false, when read-only
+ */
+LIB61850_API bool
+IedServerConfig_getReportSetting(IedServerConfig self, uint8_t setting);
+
/**
* An opaque handle for an IED server instance
*/
@@ -1357,6 +1388,26 @@ ControlAction_getOrIdent(ControlAction self, int* orIdentSize);
LIB61850_API int
ControlAction_getCtlNum(ControlAction self);
+/**
+ * \brief Gets the synchroCheck bit provided by the client
+ *
+ * \param self the control action instance
+ *
+ * \return the synchroCheck bit
+ */
+LIB61850_API bool
+ControlAction_getSynchroCheck(ControlAction self);
+
+/**
+ * \brief Gets the interlockCheck bit provided by the client
+ *
+ * \param self the control action instance
+ *
+ * \return the interlockCheck bit
+ */
+LIB61850_API bool
+ControlAction_getInterlockCheck(ControlAction self);
+
/**
* \brief Check if the control callback is called by a select or operate command
*
diff --git a/src/iec61850/inc_private/ied_server_private.h b/src/iec61850/inc_private/ied_server_private.h
index e6f6e412..d5555659 100644
--- a/src/iec61850/inc_private/ied_server_private.h
+++ b/src/iec61850/inc_private/ied_server_private.h
@@ -3,7 +3,7 @@
*
* Library private function definitions for IedServer.
*
- * Copyright 2013-2018 Michael Zillgith
+ * Copyright 2013-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -50,6 +50,7 @@ struct sIedServer
bool enableBRCBResvTms;
bool enableOwnerForRCB;
bool syncIntegrityReportTimes;
+ uint8_t rcbSettingsWritable;
#endif
#if (CONFIG_MMS_THREADLESS_STACK != 1)
diff --git a/src/iec61850/inc_private/mms_mapping_internal.h b/src/iec61850/inc_private/mms_mapping_internal.h
index 3e896495..95727449 100644
--- a/src/iec61850/inc_private/mms_mapping_internal.h
+++ b/src/iec61850/inc_private/mms_mapping_internal.h
@@ -325,7 +325,10 @@ struct sMmsMapping {
/* flag indicates if data model is locked --> prevents reports to be sent */
bool isModelLocked;
+
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore isModelLockedMutex;
+#endif /* (CONFIG_MMS_THREADLESS_STACK != 1) */
IedServer iedServer;
diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c
index f09e42b2..2350984e 100644
--- a/src/iec61850/server/impl/ied_server.c
+++ b/src/iec61850/server/impl/ied_server.c
@@ -483,12 +483,19 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio
self->enableBRCBResvTms = serverConfiguration->enableResvTmsForBRCB;
self->enableOwnerForRCB = serverConfiguration->enableOwnerForRCB;
self->syncIntegrityReportTimes = serverConfiguration->syncIntegrityReportTimes;
+ self->rcbSettingsWritable = serverConfiguration->reportSettingsWritable;
}
else {
self->reportBufferSizeBRCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE;
self->reportBufferSizeURCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE;
self->enableOwnerForRCB = false;
self->syncIntegrityReportTimes = false;
+ self->rcbSettingsWritable = IEC61850_REPORTSETTINGS_RPT_ID +
+ IEC61850_REPORTSETTINGS_BUF_TIME +
+ IEC61850_REPORTSETTINGS_DATSET +
+ IEC61850_REPORTSETTINGS_TRG_OPS +
+ IEC61850_REPORTSETTINGS_OPT_FIELDS +
+ IEC61850_REPORTSETTINGS_INTG_PD;
#if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1)
self->enableBRCBResvTms = true;
#else
@@ -814,11 +821,15 @@ IedServer_lockDataModel(IedServer self)
{
MmsServer_lockModel(self->mmsServer);
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->mmsMapping->isModelLockedMutex);
+#endif
self->mmsMapping->isModelLocked = true;
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->mmsMapping->isModelLockedMutex);
+#endif
}
void
@@ -832,13 +843,17 @@ IedServer_unlockDataModel(IedServer self)
/* check if reports have to be sent! */
Reporting_processReportEventsAfterUnlock(self->mmsMapping);
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->mmsMapping->isModelLockedMutex);
+#endif
MmsServer_unlockModel(self->mmsServer);
self->mmsMapping->isModelLocked = false;
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->mmsMapping->isModelLockedMutex);
+#endif
}
#if (CONFIG_IEC61850_CONTROL_SERVICE == 1)
diff --git a/src/iec61850/server/impl/ied_server_config.c b/src/iec61850/server/impl/ied_server_config.c
index fdc38f8e..673f58b1 100644
--- a/src/iec61850/server/impl/ied_server_config.c
+++ b/src/iec61850/server/impl/ied_server_config.c
@@ -1,7 +1,7 @@
/*
* ied_server_config.c
*
- * Copyright 2018-2022 Michael Zillgith
+ * Copyright 2018-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -59,6 +59,12 @@ IedServerConfig_create()
self->enableResvTmsForBRCB = true;
self->enableOwnerForRCB = false;
self->syncIntegrityReportTimes = false;
+ self->reportSettingsWritable = IEC61850_REPORTSETTINGS_RPT_ID +
+ IEC61850_REPORTSETTINGS_BUF_TIME +
+ IEC61850_REPORTSETTINGS_DATSET +
+ IEC61850_REPORTSETTINGS_TRG_OPS +
+ IEC61850_REPORTSETTINGS_OPT_FIELDS +
+ IEC61850_REPORTSETTINGS_INTG_PD;
}
return self;
@@ -264,3 +270,34 @@ IedServerConfig_getSyncIntegrityReportTimes(IedServerConfig self)
{
return self->syncIntegrityReportTimes;
}
+
+static void
+configureSetting(IedServerConfig self, uint8_t flags, uint8_t setting, bool value)
+{
+ if (flags & setting)
+ {
+ if (value) {
+ self->reportSettingsWritable |= setting;
+ }
+ else {
+ self->reportSettingsWritable &= ~setting;
+ }
+ }
+}
+
+void
+IedServerConfig_setReportSetting(IedServerConfig self, uint8_t setting, bool isDyn)
+{
+ configureSetting(self, setting, IEC61850_REPORTSETTINGS_RPT_ID, isDyn);
+ configureSetting(self, setting, IEC61850_REPORTSETTINGS_BUF_TIME, isDyn);
+ configureSetting(self, setting, IEC61850_REPORTSETTINGS_DATSET, isDyn);
+ configureSetting(self, setting, IEC61850_REPORTSETTINGS_TRG_OPS, isDyn);
+ configureSetting(self, setting, IEC61850_REPORTSETTINGS_OPT_FIELDS, isDyn);
+ configureSetting(self, setting, IEC61850_REPORTSETTINGS_INTG_PD, isDyn);
+}
+
+bool
+IedServerConfig_getReportSetting(IedServerConfig self, uint8_t setting)
+{
+ return (self->reportSettingsWritable & setting);
+}
diff --git a/src/iec61850/server/mms_mapping/control.c b/src/iec61850/server/mms_mapping/control.c
index 05d2d074..bcbb7d22 100644
--- a/src/iec61850/server/mms_mapping/control.c
+++ b/src/iec61850/server/mms_mapping/control.c
@@ -2498,6 +2498,22 @@ ControlAction_getCtlNum(ControlAction self)
return -1;
}
+bool
+ControlAction_getSynchroCheck(ControlAction self)
+{
+ ControlObject* controlObject = (ControlObject*) self;
+
+ return (bool)(controlObject->synchroCheck);
+}
+
+bool
+ControlAction_getInterlockCheck(ControlAction self)
+{
+ ControlObject* controlObject = (ControlObject*) self;
+
+ return (bool)(controlObject->interlockCheck);
+}
+
bool
ControlAction_isSelect(ControlAction self)
{
diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c
index 096c90c7..707e8b57 100644
--- a/src/iec61850/server/mms_mapping/mms_mapping.c
+++ b/src/iec61850/server/mms_mapping/mms_mapping.c
@@ -3670,7 +3670,9 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag)
{
LinkedList element = self->reportControls;
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->isModelLockedMutex);
+#endif
bool modelLocked = self->isModelLocked;
@@ -3686,8 +3688,7 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag)
continue;
break;
case REPORT_CONTROL_VALUE_CHANGED:
- if (((rc->triggerOps & TRG_OPT_DATA_CHANGED) == 0) &&
- ((rc->triggerOps & TRG_OPT_DATA_UPDATE) == 0))
+ if ((rc->triggerOps & TRG_OPT_DATA_CHANGED) == 0)
continue;
break;
case REPORT_CONTROL_QUALITY_CHANGED:
@@ -3708,7 +3709,9 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag)
Reporting_processReportEventsAfterUnlock(self);
}
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->isModelLockedMutex);
+#endif
}
#endif /* (CONFIG_IEC61850_REPORT_SERVICE == 1) */
@@ -3729,13 +3732,17 @@ MmsMapping_triggerGooseObservers(MmsMapping* self, MmsValue* value)
if (DataSet_isMemberValue(dataSet, value, NULL)) {
MmsGooseControlBlock_setStateChangePending(gcb);
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->isModelLockedMutex);
+#endif
if (self->isModelLocked == false) {
MmsGooseControlBlock_publishNewState(gcb);
}
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->isModelLockedMutex);
+#endif
}
}
}
diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c
index ad60bef3..cba98b86 100644
--- a/src/iec61850/server/mms_mapping/reporting.c
+++ b/src/iec61850/server/mms_mapping/reporting.c
@@ -1909,6 +1909,9 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
if (updateReportDataset(self, rc, NULL, connection)) {
if (rc->reserved == false) {
+
+ rc->resvTms = RESV_TMS_IMPLICIT_VALUE;
+
reserveRcb(rc, connection);
if (self->rcbEventHandler) {
@@ -2088,6 +2091,12 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
}
else if (strcmp(elementName, "DatSet") == 0) {
+ if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_DATSET))
+ {
+ retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
+ goto exit_function;
+ }
+
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock);
#endif
@@ -2135,6 +2144,12 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
}
else if (strcmp(elementName, "IntgPd") == 0) {
+ if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_INTG_PD))
+ {
+ retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
+ goto exit_function;
+ }
+
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock);
#endif
@@ -2182,6 +2197,12 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
}
else if (strcmp(elementName, "TrgOps") == 0) {
+ if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_TRG_OPS))
+ {
+ retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
+ goto exit_function;
+ }
+
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock);
#endif
@@ -2255,6 +2276,12 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
else if (strcmp(elementName, "BufTm") == 0) {
+ if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_BUF_TIME))
+ {
+ retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
+ goto exit_function;
+ }
+
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock);
#endif
@@ -2288,6 +2315,12 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
}
else if (strcmp(elementName, "RptID") == 0) {
+ if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_RPT_ID))
+ {
+ retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
+ goto exit_function;
+ }
+
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock);
#endif
@@ -2395,6 +2428,14 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
goto exit_function;
}
+ else if (strcmp(elementName, "OptFlds") == 0) {
+
+ if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_OPT_FIELDS))
+ {
+ retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
+ goto exit_function;
+ }
+ }
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock);
@@ -2445,7 +2486,6 @@ exit_function:
}
}
-
}
else if (rc->resvTms == -1) {
if (rc->reserved == false) {
@@ -3877,7 +3917,9 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs)
void
Reporting_processReportEvents(MmsMapping* self, uint64_t currentTimeInMs)
{
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->isModelLockedMutex);
+#endif
if (self->isModelLocked == false) {
@@ -3894,7 +3936,9 @@ Reporting_processReportEvents(MmsMapping* self, uint64_t currentTimeInMs)
}
}
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->isModelLockedMutex);
+#endif
}
/*
diff --git a/src/mms/inc/iso_connection_parameters.h b/src/mms/inc/iso_connection_parameters.h
index cdd04bcb..3514f437 100644
--- a/src/mms/inc/iso_connection_parameters.h
+++ b/src/mms/inc/iso_connection_parameters.h
@@ -145,6 +145,9 @@ struct sIsoConnectionParameters
const char* hostname;
int tcpPort;
+ const char* localIpAddress;
+ int localTcpPort;
+
uint8_t remoteApTitle[10];
int remoteApTitleLen;
int remoteAEQualifier;
@@ -215,6 +218,20 @@ IsoConnectionParameters_setAcseAuthenticationParameter(IsoConnectionParameters s
LIB61850_API void
IsoConnectionParameters_setTcpParameters(IsoConnectionParameters self, const char* hostname, int tcpPort);
+/**
+* \brief Set Local TCP parameters (FOR LIBRARY INTERNAL USE)
+*
+* NOTE: This function used internally by the MMS Client library. When using the MMS or IEC 61850 API
+* there should be no reason for the user to call this function
+*
+* \param self the IsoConnectionParameters instance
+* \param localIpAddress the hostname of local IP address of the server
+* \param localTcpPort the local TCP port number of the server
+*/
+LIB61850_API void
+IsoConnectionParameters_setLocalTcpParameters(IsoConnectionParameters self, const char* localIpAddress, int localTcpPort);
+
+
/**
* \brief set the remote AP-Title and AE-Qualifier
*
diff --git a/src/mms/inc/mms_client_connection.h b/src/mms/inc/mms_client_connection.h
index 70bedc28..86f12c9b 100644
--- a/src/mms/inc/mms_client_connection.h
+++ b/src/mms/inc/mms_client_connection.h
@@ -648,6 +648,23 @@ MmsConnection_writeVariableAsync(MmsConnection self, uint32_t* usedInvokeId, Mms
MmsConnection_WriteVariableHandler handler, void* parameter);
+/**
+ * \brief Write a single variable to the server (using component alternate access)
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param domainId the domain name of the variable to be written
+ * \param itemId name of the variable to be written
+ * \param componentId the name of the variable component
+ * \param value value of the variable to be written
+ *
+ * \return when successful, the data access error value returned by the server
+ */
+LIB61850_API MmsDataAccessError
+MmsConnection_writeVariableComponent(MmsConnection self, MmsError* mmsError,
+ const char* domainId, const char* itemId,
+ const char* componentId, MmsValue* value);
+
/**
* \brief Write a single array element with a component to an array type variable
*
@@ -672,6 +689,11 @@ MmsConnection_writeSingleArrayElementWithComponentAsync(MmsConnection self, uint
uint32_t arrayIndex, const char* componentId, MmsValue* value,
MmsConnection_WriteVariableHandler handler, void* parameter);
+LIB61850_API void
+MmsConnection_writeVariableComponentAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
+ const char* domainId, const char* itemId, const char* componentId, MmsValue* value,
+ MmsConnection_WriteVariableHandler handler, void* parameter);
+
/**
* \brief Write a single array element or a sub array to an array type variable
*
diff --git a/src/mms/inc_private/cotp.h b/src/mms/inc_private/cotp.h
index b7877cc8..3cbe2681 100644
--- a/src/mms/inc_private/cotp.h
+++ b/src/mms/inc_private/cotp.h
@@ -114,4 +114,7 @@ CotpConnection_getRemoteRef(CotpConnection* self);
LIB61850_INTERNAL int
CotpConnection_getLocalRef(CotpConnection* self);
+LIB61850_INTERNAL void
+CotpConnection_flushBuffer(CotpConnection* self);
+
#endif /* COTP_H_ */
diff --git a/src/mms/inc_private/mms_client_internal.h b/src/mms/inc_private/mms_client_internal.h
index 57243230..aa0e60f3 100644
--- a/src/mms/inc_private/mms_client_internal.h
+++ b/src/mms/inc_private/mms_client_internal.h
@@ -272,6 +272,11 @@ mmsClient_createWriteRequestArray(uint32_t invokeId, const char* domainId, const
int startIndex, int elementCount,
MmsValue* value, ByteBuffer* writeBuffer);
+LIB61850_INTERNAL int
+mmsClient_createWriteRequestComponent(uint32_t invokeId, const char* domainId, const char* itemId, const char* component,
+ MmsValue* value,
+ ByteBuffer* writeBuffer);
+
LIB61850_INTERNAL int
mmsClient_createWriteRequestAlternateAccessSingleIndexComponent(uint32_t invokeId, const char* domainId, const char* itemId,
uint32_t arrayIndex, const char* component,
diff --git a/src/mms/iso_client/iso_client_connection.c b/src/mms/iso_client/iso_client_connection.c
index cb58e98f..4f37176b 100644
--- a/src/mms/iso_client/iso_client_connection.c
+++ b/src/mms/iso_client/iso_client_connection.c
@@ -692,8 +692,13 @@ IsoClientConnection_associateAsync(IsoClientConnection self, uint32_t connectTim
/* set timeout for connect */
self->nextReadTimeout = Hal_getTimeInMs() + connectTimeoutInMs;
+ /* Connect to Local Ip Address*/
+ if (self->parameters->localIpAddress) {
+ Socket_bind(self->socket, self->parameters->localIpAddress, self->parameters->localTcpPort);
+ }
+
if (Socket_connectAsync(self->socket, self->parameters->hostname, self->parameters->tcpPort) == false) {
-
+
Socket_destroy(self->socket);
self->socket = NULL;
@@ -704,9 +709,9 @@ IsoClientConnection_associateAsync(IsoClientConnection self, uint32_t connectTim
success = false;
}
-
+
Semaphore_post(self->tickMutex);
-
+
return success;
}
diff --git a/src/mms/iso_common/iso_connection_parameters.c b/src/mms/iso_common/iso_connection_parameters.c
index 3fdf32c9..aa5334b3 100644
--- a/src/mms/iso_common/iso_connection_parameters.c
+++ b/src/mms/iso_common/iso_connection_parameters.c
@@ -104,6 +104,18 @@ IsoConnectionParameters_setTcpParameters(IsoConnectionParameters self, const cha
self->tcpPort = tcpPort;
}
+void
+IsoConnectionParameters_setLocalTcpParameters(IsoConnectionParameters self, const char* localIpAddress, int localTcpPort)
+{
+ if (self) {
+ if (localIpAddress) {
+ self->localIpAddress = strdup(localIpAddress);
+ self->localTcpPort = localTcpPort;
+ }
+ }
+}
+
+
void
IsoConnectionParameters_setRemoteApTitle(IsoConnectionParameters self, const char* apTitle, int aeQualifier)
{
diff --git a/src/mms/iso_cotp/cotp.c b/src/mms/iso_cotp/cotp.c
index f57e05bc..2b4e43e6 100644
--- a/src/mms/iso_cotp/cotp.c
+++ b/src/mms/iso_cotp/cotp.c
@@ -5,7 +5,7 @@
*
* Partial implementation of the ISO 8073 COTP (ISO TP0) protocol for MMS.
*
- * Copyright 2013-2018 Michael Zillgith
+ * Copyright 2013-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -174,6 +174,38 @@ writeToSocket(CotpConnection* self, uint8_t* buf, int size)
#endif
}
+static bool
+flushBuffer(CotpConnection* self)
+{
+ if (self->socketExtensionBufferFill > 0) {
+
+ int sentBytes = writeToSocket(self, self->socketExtensionBuffer, self->socketExtensionBufferFill);
+
+ if (sentBytes > 0) {
+
+ if (sentBytes != self->socketExtensionBufferFill) {
+ int target = 0;
+ int i;
+ uint8_t* buf = self->socketExtensionBuffer;
+
+ for (i = sentBytes; i < self->socketExtensionBufferFill; i++) {
+ buf[target++] = buf[i];
+ }
+
+ self->socketExtensionBufferFill = self->socketExtensionBufferFill - sentBytes;
+ }
+ else {
+ self->socketExtensionBufferFill = 0;
+ }
+ }
+ else if (sentBytes == -1) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
static bool
sendBuffer(CotpConnection* self)
{
@@ -182,7 +214,15 @@ sendBuffer(CotpConnection* self)
bool retVal = false;
- int sentBytes = writeToSocket(self, buffer, remainingSize);
+ if (flushBuffer(self) == false) {
+ goto exit_function;
+ }
+
+ int sentBytes = 0;
+
+ if (self->socketExtensionBufferFill == 0) {
+ sentBytes = writeToSocket(self, buffer, remainingSize);
+ }
if (sentBytes == -1)
goto exit_function;
@@ -215,33 +255,6 @@ exit_function:
return retVal;
}
-static void
-flushBuffer(CotpConnection* self)
-{
- if (self->socketExtensionBufferFill > 0) {
-
- int sentBytes = writeToSocket(self, self->socketExtensionBuffer, self->socketExtensionBufferFill);
-
- if (sentBytes > 0) {
-
- if (sentBytes != self->socketExtensionBufferFill) {
- int target = 0;
- int i;
- uint8_t* buf = self->socketExtensionBuffer;
-
- for (i = sentBytes; i < self->socketExtensionBufferFill; i++) {
- buf[target++] = buf[i];
- }
-
- self->socketExtensionBufferFill = self->socketExtensionBufferFill - sentBytes;
- }
- else {
- self->socketExtensionBufferFill = 0;
- }
- }
- }
-}
-
CotpIndication
CotpConnection_sendDataMessage(CotpConnection* self, BufferChain payload)
{
@@ -262,7 +275,9 @@ CotpConnection_sendDataMessage(CotpConnection* self, BufferChain payload)
int totalSize = (fragments * (COTP_DATA_HEADER_SIZE + 4)) + payload->length;
/* try to flush extension buffer */
- flushBuffer(self);
+ if (flushBuffer(self) == false) {
+ return COTP_ERROR;
+ }
/* check if totalSize will fit in extension buffer */
if (self->socketExtensionBuffer) {
@@ -281,7 +296,7 @@ CotpConnection_sendDataMessage(CotpConnection* self, BufferChain payload)
int currentChainIndex = 0;
if (DEBUG_COTP)
- printf("\nCOTP: nextBufferPart: len:%i partLen:%i\n", currentChain->length, currentChain->partLength);
+ printf("COTP: nextBufferPart: len:%i partLen:%i\n", currentChain->length, currentChain->partLength);
uint8_t* buffer = self->writeBuffer->buffer;
@@ -307,7 +322,7 @@ CotpConnection_sendDataMessage(CotpConnection* self, BufferChain payload)
if (currentChainIndex >= currentChain->partLength) {
currentChain = currentChain->nextPart;
if (DEBUG_COTP)
- printf("\nCOTP: nextBufferPart: len:%i partLen:%i\n", currentChain->length, currentChain->partLength);
+ printf("COTP: nextBufferPart: len:%i partLen:%i\n", currentChain->length, currentChain->partLength);
currentChainIndex = 0;
}
@@ -756,6 +771,13 @@ readFromSocket(CotpConnection* self, uint8_t* buf, int size)
#endif
}
+void
+CotpConnection_flushBuffer(CotpConnection* self)
+{
+ if (self->socketExtensionBufferFill > 0)
+ flushBuffer(self);
+}
+
TpktState
CotpConnection_readToTpktBuffer(CotpConnection* self)
{
@@ -765,6 +787,14 @@ CotpConnection_readToTpktBuffer(CotpConnection* self)
assert (bufferSize > 4);
+ if (self->socketExtensionBufferFill > 0) {
+ if (flushBuffer(self) == false)
+ goto exit_error;
+
+ if (self->socketExtensionBufferFill > 0)
+ goto exit_waiting;
+ }
+
int readBytes;
if (bufPos < 4) {
diff --git a/src/mms/iso_mms/client/mms_client_connection.c b/src/mms/iso_mms/client/mms_client_connection.c
index e1bb0039..cc63582b 100644
--- a/src/mms/iso_mms/client/mms_client_connection.c
+++ b/src/mms/iso_mms/client/mms_client_connection.c
@@ -4384,6 +4384,69 @@ exit_function:
return;
}
+MmsDataAccessError
+MmsConnection_writeVariableComponent(MmsConnection self, MmsError* mmsError,
+ const char* domainId, const char* itemId,
+ const char* componentId, MmsValue* value)
+{
+ struct writeVariableParameters parameter;
+
+ MmsError err = MMS_ERROR_NONE;
+
+ parameter.waitForResponse = Semaphore_create(1);
+ parameter.err = MMS_ERROR_NONE;
+ parameter.accessError = DATA_ACCESS_ERROR_SUCCESS;
+
+ Semaphore_wait(parameter.waitForResponse);
+
+ MmsConnection_writeVariableComponentAsync(self, NULL, &err, domainId, itemId, componentId, value, writeVariableHandler, ¶meter);
+
+ if (err == MMS_ERROR_NONE) {
+ Semaphore_wait(parameter.waitForResponse);
+
+ err = parameter.err;
+ }
+
+ Semaphore_destroy(parameter.waitForResponse);
+
+ if (mmsError)
+ *mmsError = err;
+
+ return parameter.accessError;
+}
+
+void
+MmsConnection_writeVariableComponentAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
+ const char* domainId, const char* itemId, const char* componentId, MmsValue* value,
+ MmsConnection_WriteVariableHandler handler, void* parameter)
+{
+ if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) {
+ if (mmsError)
+ *mmsError = MMS_ERROR_CONNECTION_LOST;
+ goto exit_function;
+ }
+
+ ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient);
+
+ uint32_t invokeId = getNextInvokeId(self);
+
+ if (usedInvokeId)
+ *usedInvokeId = invokeId;
+
+ mmsClient_createWriteRequestComponent(invokeId, domainId, itemId, componentId, value, payload);
+
+ MmsClientInternalParameter intParam;
+ intParam.ptr = NULL;
+
+ MmsError err = sendAsyncRequest(self, invokeId, payload, MMS_CALL_TYPE_WRITE_VARIABLE, handler, parameter, intParam);
+
+ if (mmsError)
+ *mmsError = err;
+
+exit_function:
+ return;
+}
+
struct writeMultipleVariablesParameter
{
Semaphore sem;
diff --git a/src/mms/iso_mms/client/mms_client_write.c b/src/mms/iso_mms/client/mms_client_write.c
index e99a6e1b..fc2b3d62 100644
--- a/src/mms/iso_mms/client/mms_client_write.c
+++ b/src/mms/iso_mms/client/mms_client_write.c
@@ -529,6 +529,63 @@ mmsClient_createWriteRequestArray(uint32_t invokeId, const char* domainId, const
return rval.encoded;
}
+int
+mmsClient_createWriteRequestComponent(uint32_t invokeId, const char* domainId, const char* itemId, const char* component,
+ MmsValue* value,
+ ByteBuffer* writeBuffer)
+{
+ MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId);
+
+ mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present =
+ ConfirmedServiceRequest_PR_write;
+ WriteRequest_t* request =
+ &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.write);
+
+ /* Create list of variable specifications */
+ request->variableAccessSpecification.present = VariableAccessSpecification_PR_listOfVariable;
+ request->variableAccessSpecification.choice.listOfVariable.list.count = 1;
+ request->variableAccessSpecification.choice.listOfVariable.list.array =
+ (ListOfVariableSeq_t**) GLOBAL_CALLOC(1, sizeof(ListOfVariableSeq_t*));
+
+ ListOfVariableSeq_t* variableIdentifier = createNewDomainVariableSpecification(domainId, itemId);
+
+ request->variableAccessSpecification.choice.listOfVariable.list.array[0] = variableIdentifier;
+
+ variableIdentifier->alternateAccess = mmsClient_createAlternateAccessComponent(component);
+
+ /* Create list of typed data values */
+ request->listOfData.list.count = 1;
+ request->listOfData.list.size = 1;
+ request->listOfData.list.array = (Data_t**) GLOBAL_CALLOC(1, sizeof(struct Data*));
+ request->listOfData.list.array[0] = mmsMsg_createBasicDataElement(value);
+
+ /* Encode complete ASN1 structure */
+
+ asn_enc_rval_t rval;
+
+ rval = der_encode(&asn_DEF_MmsPdu, mmsPdu,
+ (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer);
+
+ /* Free ASN structure */
+ mmsClient_deleteAlternateAccess(variableIdentifier->alternateAccess);
+ request->variableAccessSpecification.choice.listOfVariable.list.count = 0;
+
+ GLOBAL_FREEMEM(request->variableAccessSpecification.choice.listOfVariable.list.array[0]);
+ GLOBAL_FREEMEM(request->variableAccessSpecification.choice.listOfVariable.list.array);
+ request->variableAccessSpecification.choice.listOfVariable.list.array = 0;
+
+ request->listOfData.list.count = 0;
+
+ deleteDataElement(request->listOfData.list.array[0]);
+
+ GLOBAL_FREEMEM(request->listOfData.list.array);
+ request->listOfData.list.array = 0;
+
+ asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0);
+
+ return rval.encoded;
+}
+
int
mmsClient_createWriteRequestAlternateAccessSingleIndexComponent(uint32_t invokeId, const char* domainId, const char* itemId,
uint32_t arrayIndex, const char* component,
diff --git a/src/mms/iso_mms/common/mms_value.c b/src/mms/iso_mms/common/mms_value.c
index 29e96e4d..549619a8 100644
--- a/src/mms/iso_mms/common/mms_value.c
+++ b/src/mms/iso_mms/common/mms_value.c
@@ -2205,7 +2205,7 @@ MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize)
const char* currentStr = MmsValue_printToBuffer((const MmsValue*) MmsValue_getElement(self, i), buffer + bufPos, bufferSize - bufPos);
- bufPos += strlen(currentStr);
+ bufPos += strnlen(currentStr, bufferSize - bufPos);
if (bufPos >= bufferSize)
break;
@@ -2240,9 +2240,13 @@ MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize)
int size = MmsValue_getBitStringSize(self);
/* fill buffer with zeros */
- if (size > bufferSize) {
+ if (size + 1 > bufferSize) {
memset(buffer, 0, bufferSize);
- break;
+
+ size = bufferSize - 1;
+
+ if (size < 1)
+ break;
}
int i;
diff --git a/src/mms/iso_mms/server/mms_write_service.c b/src/mms/iso_mms/server/mms_write_service.c
index 124de4fe..5f68ab48 100644
--- a/src/mms/iso_mms/server/mms_write_service.c
+++ b/src/mms/iso_mms/server/mms_write_service.c
@@ -479,6 +479,57 @@ handleWriteNamedVariableListRequest(
}
+static MmsVariableSpecification*
+getComponent(MmsServerConnection connection, MmsDomain* domain, AlternateAccess_t* alternateAccess, MmsVariableSpecification* namedVariable, char* variableName)
+{
+ MmsVariableSpecification* retValue = NULL;
+
+ 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) {
+
+ int i;
+
+ for (i = 0; i < namedVariable->typeSpec.structure.elementCount; i++) {
+
+ if ((int) strlen(namedVariable->typeSpec.structure.elements[i]->name)
+ == component.size) {
+ if (!strncmp(namedVariable->typeSpec.structure.elements[i]->name,
+ (char*) component.buf, component.size))
+ {
+ 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) {
+ retValue =
+ getComponent(connection, domain,
+ alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess,
+ namedVariable->typeSpec.structure.elements[i],
+ variableName);
+ }
+ else {
+ retValue = namedVariable->typeSpec.structure.elements[i];
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+exit_function:
+ return retValue;
+}
void
mmsServer_handleWriteRequest(
@@ -604,15 +655,21 @@ mmsServer_handleWriteRequest(
AlternateAccess_t* alternateAccess = varSpec->alternateAccess;
if (alternateAccess != NULL) {
- if (variable->type != MMS_ARRAY) {
+
+ if ((variable->type == MMS_STRUCTURE) && (mmsServer_isComponentAccess(alternateAccess) == false)) {
accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
continue;
}
- if (!mmsServer_isIndexAccess(alternateAccess)) {
+ if ((variable->type == MMS_ARRAY) && (mmsServer_isIndexAccess(alternateAccess) == false)) {
accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED;
continue;
}
+
+ if (variable->type != MMS_ARRAY && variable->type != MMS_STRUCTURE) {
+ accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
+ continue;
+ }
}
Data_t* dataElement = writeRequest->listOfData.list.array[i];
@@ -626,62 +683,75 @@ mmsServer_handleWriteRequest(
if (alternateAccess != NULL) {
- if (domain == NULL)
+ if (domain == NULL)
domain = (MmsDomain*) device;
- MmsValue* cachedArray = MmsServer_getValueFromCache(connection->server, domain, nameIdStr);
+ if (mmsServer_isIndexAccess(alternateAccess)) {
+ MmsValue* cachedArray = MmsServer_getValueFromCache(connection->server, domain, nameIdStr);
- if (cachedArray == NULL) {
- accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
- goto end_of_main_loop;
- }
-
- int index = mmsServer_getLowIndex(alternateAccess);
- int numberOfElements = mmsServer_getNumberOfElements(alternateAccess);
-
- if (numberOfElements == 0) { /* select single array element with index */
-
- MmsValue* elementValue = MmsValue_getElement(cachedArray, index);
-
- if (elementValue == NULL) {
+ if (cachedArray == NULL) {
accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
goto end_of_main_loop;
}
- if (MmsValue_update(elementValue, value) == false) {
- accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
- goto end_of_main_loop;
- }
- }
- else { /* select sub-array with start-index and number-of-elements */
+ int index = mmsServer_getLowIndex(alternateAccess);
+ int numberOfElements = mmsServer_getNumberOfElements(alternateAccess);
- if (MmsValue_getType(value) != MMS_ARRAY) {
- accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
- goto end_of_main_loop;
- }
+ if (numberOfElements == 0) { /* select single array element with index */
- int elementNo;
+ MmsValue* elementValue = MmsValue_getElement(cachedArray, index);
- for (elementNo = 0; elementNo < numberOfElements; elementNo++) {
- MmsValue* newElement = MmsValue_getElement(value, elementNo);
- MmsValue* elementValue = MmsValue_getElement(cachedArray, index++);
+ if (elementValue == NULL) {
+ accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
+ goto end_of_main_loop;
+ }
- if ((elementValue == NULL) || (newElement == NULL) ) {
+ if (MmsValue_update(elementValue, value) == false) {
accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
goto end_of_main_loop;
}
+ }
+ else { /* select sub-array with start-index and number-of-elements */
- if (MmsValue_update(elementValue, newElement) == false) {
+ 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++) {
+ MmsValue* newElement = MmsValue_getElement(value, elementNo);
+ MmsValue* elementValue = MmsValue_getElement(cachedArray, index++);
+
+ if ((elementValue == NULL) || (newElement == NULL) ) {
+ accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
+ goto end_of_main_loop;
+ }
+ else { /* select sub-array with start-index and number-of-elements */
+ if (MmsValue_update(elementValue, newElement) == false) {
+ accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
+ goto end_of_main_loop;
+ }
+ }
+ }
}
- }
- accessResults[i] = DATA_ACCESS_ERROR_SUCCESS;
- goto end_of_main_loop;
+ accessResults[i] = DATA_ACCESS_ERROR_SUCCESS;
+ goto end_of_main_loop;
+ }
+ else if (mmsServer_isComponentAccess(alternateAccess)) {
+ variable = getComponent(connection, domain, alternateAccess, variable, nameIdStr);
+ if (variable == NULL) {
+ accessResults[i] = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT;
+ goto end_of_main_loop;
+ }
+ }
+ else {
+ accessResults[i] = DATA_ACCESS_ERROR_SUCCESS;
+ goto end_of_main_loop;
+ }
}
/* Check for correct type */
diff --git a/src/mms/iso_server/iso_connection.c b/src/mms/iso_server/iso_connection.c
index 7f997a9b..795b0636 100644
--- a/src/mms/iso_server/iso_connection.c
+++ b/src/mms/iso_server/iso_connection.c
@@ -1,7 +1,7 @@
/*
* iso_connection.c
*
- * Copyright 2013-2022 Michael Zillgith
+ * Copyright 2013-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -160,6 +160,8 @@ IsoConnection_removeFromHandleSet(const IsoConnection self, HandleSet handles)
void
IsoConnection_callTickHandler(IsoConnection self)
{
+ CotpConnection_flushBuffer(self->cotpConnection);
+
if (self->tickHandler) {
self->tickHandler(self->handlerParameter);
}
@@ -171,10 +173,7 @@ IsoConnection_handleTcpConnection(IsoConnection self, bool isSingleThread)
#if (CONFIG_MMS_SINGLE_THREADED != 1)
if (isSingleThread == false) {
- /* call tick handler */
- if (self->tickHandler) {
- self->tickHandler(self->handlerParameter);
- }
+ IsoConnection_callTickHandler(self);
if (Handleset_waitReady(self->handleSet, 10) < 1)
goto exit_function;
diff --git a/src/mms/iso_server/iso_server.c b/src/mms/iso_server/iso_server.c
index 62c43ebb..a2daa6ad 100644
--- a/src/mms/iso_server/iso_server.c
+++ b/src/mms/iso_server/iso_server.c
@@ -455,7 +455,6 @@ exit_function:
return success;
}
-
/** used by single and multi-threaded versions
*
* \param isSingleThread when true server is running in single thread or non-thread mode