diff --git a/examples/server_example_substitution/server_example_substitution.c b/examples/server_example_substitution/server_example_substitution.c index 0050f04f..2f41d892 100644 --- a/examples/server_example_substitution/server_example_substitution.c +++ b/examples/server_example_substitution/server_example_substitution.c @@ -2,7 +2,8 @@ * server_example_substitution.c * * - How to use the IEC 61850 substitution service - * - Two data objects can be substituted: + * - How to use the blocking service + * - Two data objects can be substituted and/or blocked: * -- GGIO1.AnIn1 * -- GGIO1.Ind1 */ @@ -24,6 +25,8 @@ static IedServer iedServer = NULL; static bool subsAnIn1 = false; static bool subsInd1 = false; +static bool blkEnaAnIn1 = false; +static bool blkEnaInd1 = false; static float an1 = 0.f; static uint64_t timestamp = 0; @@ -35,7 +38,6 @@ sigint_handler(int signalId) running = 0; } - static void connectionHandler (IedServer self, ClientConnection connection, bool connected, void* parameter) { @@ -54,13 +56,13 @@ updateProcessValues() Timestamp_setTimeInMilliseconds(&iecTimestamp, timestamp); Timestamp_setLeapSecondKnown(&iecTimestamp, true); - if (subsAnIn1 == false) { + if ((subsAnIn1 == false) && (blkEnaAnIn1 == false)) { IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_t, &iecTimestamp); IedServer_updateQuality(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_q, QUALITY_VALIDITY_GOOD); IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_mag_f, an1); } - if (subsInd1 == false) { + if ((subsInd1 == false) && (blkEnaInd1 == false)) { IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_Ind1_t, &iecTimestamp); IedServer_updateQuality(iedServer, IEDMODEL_LD1_GGIO1_Ind1_q, QUALITY_VALIDITY_GOOD); IedServer_updateBooleanAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_Ind1_stVal, ind1); @@ -116,6 +118,23 @@ writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnect } } + else if (dataAttribute == IEDMODEL_LD1_GGIO1_AnIn1_blkEna) { + printf("Received GGIO1.AnIn1.blkEna: %i\n", MmsValue_getBoolean(value)); + + blkEnaAnIn1 = MmsValue_getBoolean(value); + + /* Update quality flags */ + + Quality quality = + Quality_fromMmsValue(IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_q)); + + if (blkEnaAnIn1) + Quality_setFlag(&quality, QUALITY_OPERATOR_BLOCKED); + else + Quality_unsetFlag(&quality, QUALITY_OPERATOR_BLOCKED); + + IedServer_updateQuality(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_q, quality); + } else if (dataAttribute == IEDMODEL_LD1_GGIO1_Ind1_subEna) { printf("Received GGIO1.Ind1.subEna: %i\n", MmsValue_getBoolean(value)); @@ -160,6 +179,23 @@ writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnect } } + else if (dataAttribute == IEDMODEL_LD1_GGIO1_Ind1_blkEna) { + printf("Received GGIO1.Ind1.blkEna: %i\n", MmsValue_getBoolean(value)); + + blkEnaInd1 = MmsValue_getBoolean(value); + + /* Update quality flags */ + + Quality quality = + Quality_fromMmsValue(IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_Ind1_q)); + + if (blkEnaAnIn1) + Quality_setFlag(&quality, QUALITY_OPERATOR_BLOCKED); + else + Quality_unsetFlag(&quality, QUALITY_OPERATOR_BLOCKED); + + IedServer_updateQuality(iedServer, IEDMODEL_LD1_GGIO1_Ind1_q, quality); + } return DATA_ACCESS_ERROR_SUCCESS; } @@ -179,10 +215,12 @@ main(int argc, char** argv) IedServer_handleWriteAccess(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_subEna, writeAccessHandler, NULL); IedServer_handleWriteAccess(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_subMag_f, writeAccessHandler, NULL); IedServer_handleWriteAccess(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_subQ, writeAccessHandler, NULL); + IedServer_handleWriteAccess(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_blkEna, writeAccessHandler, NULL); IedServer_handleWriteAccess(iedServer, IEDMODEL_LD1_GGIO1_Ind1_subEna, writeAccessHandler, NULL); IedServer_handleWriteAccess(iedServer, IEDMODEL_LD1_GGIO1_Ind1_subVal, writeAccessHandler, NULL); IedServer_handleWriteAccess(iedServer, IEDMODEL_LD1_GGIO1_Ind1_subQ, writeAccessHandler, NULL); + IedServer_handleWriteAccess(iedServer, IEDMODEL_LD1_GGIO1_Ind1_blkEna, writeAccessHandler, NULL); /* MMS server will be instructed to start listening for client connections. */ IedServer_start(iedServer, 102); @@ -225,6 +263,6 @@ main(int argc, char** argv) /* Cleanup - free all resources */ IedServer_destroy(iedServer); - return 0; + return 0; } /* main() */ diff --git a/examples/server_example_substitution/static_model.c b/examples/server_example_substitution/static_model.c index 0f8bff5d..c59ba0df 100644 --- a/examples/server_example_substitution/static_model.c +++ b/examples/server_example_substitution/static_model.c @@ -1,7 +1,7 @@ /* * static_model.c * - * automatically generated from substitution_example.icd + * automatically generated from substitution_example.cid */ #include "static_model.h" @@ -242,7 +242,7 @@ DataAttribute iedModel_LD1_LPHD1_Proxy_subID = { DataAttributeModelType, "subID", (ModelNode*) &iedModel_LD1_LPHD1_Proxy, - NULL, + (ModelNode*) &iedModel_LD1_LPHD1_Proxy_blkEna, NULL, 0, IEC61850_FC_SV, @@ -251,6 +251,19 @@ DataAttribute iedModel_LD1_LPHD1_Proxy_subID = { NULL, 0}; +DataAttribute iedModel_LD1_LPHD1_Proxy_blkEna = { + DataAttributeModelType, + "blkEna", + (ModelNode*) &iedModel_LD1_LPHD1_Proxy, + NULL, + NULL, + 0, + IEC61850_FC_BL, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + LogicalNode iedModel_LD1_MMDC1 = { LogicalNodeModelType, "MMDC1", @@ -424,7 +437,7 @@ DataAttribute iedModel_LD1_MMDC1_Watt_subID = { DataAttributeModelType, "subID", (ModelNode*) &iedModel_LD1_MMDC1_Watt, - NULL, + (ModelNode*) &iedModel_LD1_MMDC1_Watt_blkEna, NULL, 0, IEC61850_FC_SV, @@ -433,6 +446,19 @@ DataAttribute iedModel_LD1_MMDC1_Watt_subID = { NULL, 0}; +DataAttribute iedModel_LD1_MMDC1_Watt_blkEna = { + DataAttributeModelType, + "blkEna", + (ModelNode*) &iedModel_LD1_MMDC1_Watt, + NULL, + NULL, + 0, + IEC61850_FC_BL, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + DataObject iedModel_LD1_MMDC1_Amp = { DataObjectModelType, "Amp", @@ -550,7 +576,7 @@ DataAttribute iedModel_LD1_MMDC1_Amp_subID = { DataAttributeModelType, "subID", (ModelNode*) &iedModel_LD1_MMDC1_Amp, - NULL, + (ModelNode*) &iedModel_LD1_MMDC1_Amp_blkEna, NULL, 0, IEC61850_FC_SV, @@ -559,6 +585,19 @@ DataAttribute iedModel_LD1_MMDC1_Amp_subID = { NULL, 0}; +DataAttribute iedModel_LD1_MMDC1_Amp_blkEna = { + DataAttributeModelType, + "blkEna", + (ModelNode*) &iedModel_LD1_MMDC1_Amp, + NULL, + NULL, + 0, + IEC61850_FC_BL, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + DataObject iedModel_LD1_MMDC1_Vol = { DataObjectModelType, "Vol", @@ -676,7 +715,7 @@ DataAttribute iedModel_LD1_MMDC1_Vol_subID = { DataAttributeModelType, "subID", (ModelNode*) &iedModel_LD1_MMDC1_Vol, - NULL, + (ModelNode*) &iedModel_LD1_MMDC1_Vol_blkEna, NULL, 0, IEC61850_FC_SV, @@ -685,6 +724,19 @@ DataAttribute iedModel_LD1_MMDC1_Vol_subID = { NULL, 0}; +DataAttribute iedModel_LD1_MMDC1_Vol_blkEna = { + DataAttributeModelType, + "blkEna", + (ModelNode*) &iedModel_LD1_MMDC1_Vol, + NULL, + NULL, + 0, + IEC61850_FC_BL, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + LogicalNode iedModel_LD1_GGIO1 = { LogicalNodeModelType, "GGIO1", @@ -832,7 +884,7 @@ DataAttribute iedModel_LD1_GGIO1_Ind1_subID = { DataAttributeModelType, "subID", (ModelNode*) &iedModel_LD1_GGIO1_Ind1, - NULL, + (ModelNode*) &iedModel_LD1_GGIO1_Ind1_blkEna, NULL, 0, IEC61850_FC_SV, @@ -841,6 +893,19 @@ DataAttribute iedModel_LD1_GGIO1_Ind1_subID = { NULL, 0}; +DataAttribute iedModel_LD1_GGIO1_Ind1_blkEna = { + DataAttributeModelType, + "blkEna", + (ModelNode*) &iedModel_LD1_GGIO1_Ind1, + NULL, + NULL, + 0, + IEC61850_FC_BL, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + DataObject iedModel_LD1_GGIO1_AnIn1 = { DataObjectModelType, "AnIn1", @@ -958,7 +1023,7 @@ DataAttribute iedModel_LD1_GGIO1_AnIn1_subID = { DataAttributeModelType, "subID", (ModelNode*) &iedModel_LD1_GGIO1_AnIn1, - NULL, + (ModelNode*) &iedModel_LD1_GGIO1_AnIn1_blkEna, NULL, 0, IEC61850_FC_SV, @@ -967,6 +1032,19 @@ DataAttribute iedModel_LD1_GGIO1_AnIn1_subID = { NULL, 0}; +DataAttribute iedModel_LD1_GGIO1_AnIn1_blkEna = { + DataAttributeModelType, + "blkEna", + (ModelNode*) &iedModel_LD1_GGIO1_AnIn1, + NULL, + NULL, + 0, + IEC61850_FC_BL, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + extern ReportControlBlock iedModel_LD1_LLN0_report0; extern ReportControlBlock iedModel_LD1_LLN0_report1; diff --git a/examples/server_example_substitution/static_model.h b/examples/server_example_substitution/static_model.h index 1ad6a5e4..55fc628a 100644 --- a/examples/server_example_substitution/static_model.h +++ b/examples/server_example_substitution/static_model.h @@ -1,7 +1,7 @@ /* * static_model.h * - * automatically generated from substitution_example.icd + * automatically generated from substitution_example.cid */ #ifndef STATIC_MODEL_H_ @@ -32,6 +32,7 @@ extern DataAttribute iedModel_LD1_LPHD1_Proxy_subEna; extern DataAttribute iedModel_LD1_LPHD1_Proxy_subVal; extern DataAttribute iedModel_LD1_LPHD1_Proxy_subQ; extern DataAttribute iedModel_LD1_LPHD1_Proxy_subID; +extern DataAttribute iedModel_LD1_LPHD1_Proxy_blkEna; extern LogicalNode iedModel_LD1_MMDC1; extern DataObject iedModel_LD1_MMDC1_Beh; extern DataAttribute iedModel_LD1_MMDC1_Beh_stVal; @@ -47,6 +48,7 @@ extern DataAttribute iedModel_LD1_MMDC1_Watt_subMag; extern DataAttribute iedModel_LD1_MMDC1_Watt_subMag_f; extern DataAttribute iedModel_LD1_MMDC1_Watt_subQ; extern DataAttribute iedModel_LD1_MMDC1_Watt_subID; +extern DataAttribute iedModel_LD1_MMDC1_Watt_blkEna; extern DataObject iedModel_LD1_MMDC1_Amp; extern DataAttribute iedModel_LD1_MMDC1_Amp_mag; extern DataAttribute iedModel_LD1_MMDC1_Amp_mag_f; @@ -57,6 +59,7 @@ extern DataAttribute iedModel_LD1_MMDC1_Amp_subMag; extern DataAttribute iedModel_LD1_MMDC1_Amp_subMag_f; extern DataAttribute iedModel_LD1_MMDC1_Amp_subQ; extern DataAttribute iedModel_LD1_MMDC1_Amp_subID; +extern DataAttribute iedModel_LD1_MMDC1_Amp_blkEna; extern DataObject iedModel_LD1_MMDC1_Vol; extern DataAttribute iedModel_LD1_MMDC1_Vol_mag; extern DataAttribute iedModel_LD1_MMDC1_Vol_mag_f; @@ -67,6 +70,7 @@ extern DataAttribute iedModel_LD1_MMDC1_Vol_subMag; extern DataAttribute iedModel_LD1_MMDC1_Vol_subMag_f; extern DataAttribute iedModel_LD1_MMDC1_Vol_subQ; extern DataAttribute iedModel_LD1_MMDC1_Vol_subID; +extern DataAttribute iedModel_LD1_MMDC1_Vol_blkEna; extern LogicalNode iedModel_LD1_GGIO1; extern DataObject iedModel_LD1_GGIO1_Beh; extern DataAttribute iedModel_LD1_GGIO1_Beh_stVal; @@ -80,6 +84,7 @@ extern DataAttribute iedModel_LD1_GGIO1_Ind1_subEna; extern DataAttribute iedModel_LD1_GGIO1_Ind1_subVal; extern DataAttribute iedModel_LD1_GGIO1_Ind1_subQ; extern DataAttribute iedModel_LD1_GGIO1_Ind1_subID; +extern DataAttribute iedModel_LD1_GGIO1_Ind1_blkEna; extern DataObject iedModel_LD1_GGIO1_AnIn1; extern DataAttribute iedModel_LD1_GGIO1_AnIn1_mag; extern DataAttribute iedModel_LD1_GGIO1_AnIn1_mag_f; @@ -90,6 +95,7 @@ extern DataAttribute iedModel_LD1_GGIO1_AnIn1_subMag; extern DataAttribute iedModel_LD1_GGIO1_AnIn1_subMag_f; extern DataAttribute iedModel_LD1_GGIO1_AnIn1_subQ; extern DataAttribute iedModel_LD1_GGIO1_AnIn1_subID; +extern DataAttribute iedModel_LD1_GGIO1_AnIn1_blkEna; @@ -114,6 +120,7 @@ extern DataAttribute iedModel_LD1_GGIO1_AnIn1_subID; #define IEDMODEL_LD1_LPHD1_Proxy_subVal (&iedModel_LD1_LPHD1_Proxy_subVal) #define IEDMODEL_LD1_LPHD1_Proxy_subQ (&iedModel_LD1_LPHD1_Proxy_subQ) #define IEDMODEL_LD1_LPHD1_Proxy_subID (&iedModel_LD1_LPHD1_Proxy_subID) +#define IEDMODEL_LD1_LPHD1_Proxy_blkEna (&iedModel_LD1_LPHD1_Proxy_blkEna) #define IEDMODEL_LD1_MMDC1 (&iedModel_LD1_MMDC1) #define IEDMODEL_LD1_MMDC1_Beh (&iedModel_LD1_MMDC1_Beh) #define IEDMODEL_LD1_MMDC1_Beh_stVal (&iedModel_LD1_MMDC1_Beh_stVal) @@ -129,6 +136,7 @@ extern DataAttribute iedModel_LD1_GGIO1_AnIn1_subID; #define IEDMODEL_LD1_MMDC1_Watt_subMag_f (&iedModel_LD1_MMDC1_Watt_subMag_f) #define IEDMODEL_LD1_MMDC1_Watt_subQ (&iedModel_LD1_MMDC1_Watt_subQ) #define IEDMODEL_LD1_MMDC1_Watt_subID (&iedModel_LD1_MMDC1_Watt_subID) +#define IEDMODEL_LD1_MMDC1_Watt_blkEna (&iedModel_LD1_MMDC1_Watt_blkEna) #define IEDMODEL_LD1_MMDC1_Amp (&iedModel_LD1_MMDC1_Amp) #define IEDMODEL_LD1_MMDC1_Amp_mag (&iedModel_LD1_MMDC1_Amp_mag) #define IEDMODEL_LD1_MMDC1_Amp_mag_f (&iedModel_LD1_MMDC1_Amp_mag_f) @@ -139,6 +147,7 @@ extern DataAttribute iedModel_LD1_GGIO1_AnIn1_subID; #define IEDMODEL_LD1_MMDC1_Amp_subMag_f (&iedModel_LD1_MMDC1_Amp_subMag_f) #define IEDMODEL_LD1_MMDC1_Amp_subQ (&iedModel_LD1_MMDC1_Amp_subQ) #define IEDMODEL_LD1_MMDC1_Amp_subID (&iedModel_LD1_MMDC1_Amp_subID) +#define IEDMODEL_LD1_MMDC1_Amp_blkEna (&iedModel_LD1_MMDC1_Amp_blkEna) #define IEDMODEL_LD1_MMDC1_Vol (&iedModel_LD1_MMDC1_Vol) #define IEDMODEL_LD1_MMDC1_Vol_mag (&iedModel_LD1_MMDC1_Vol_mag) #define IEDMODEL_LD1_MMDC1_Vol_mag_f (&iedModel_LD1_MMDC1_Vol_mag_f) @@ -149,6 +158,7 @@ extern DataAttribute iedModel_LD1_GGIO1_AnIn1_subID; #define IEDMODEL_LD1_MMDC1_Vol_subMag_f (&iedModel_LD1_MMDC1_Vol_subMag_f) #define IEDMODEL_LD1_MMDC1_Vol_subQ (&iedModel_LD1_MMDC1_Vol_subQ) #define IEDMODEL_LD1_MMDC1_Vol_subID (&iedModel_LD1_MMDC1_Vol_subID) +#define IEDMODEL_LD1_MMDC1_Vol_blkEna (&iedModel_LD1_MMDC1_Vol_blkEna) #define IEDMODEL_LD1_GGIO1 (&iedModel_LD1_GGIO1) #define IEDMODEL_LD1_GGIO1_Beh (&iedModel_LD1_GGIO1_Beh) #define IEDMODEL_LD1_GGIO1_Beh_stVal (&iedModel_LD1_GGIO1_Beh_stVal) @@ -162,6 +172,7 @@ extern DataAttribute iedModel_LD1_GGIO1_AnIn1_subID; #define IEDMODEL_LD1_GGIO1_Ind1_subVal (&iedModel_LD1_GGIO1_Ind1_subVal) #define IEDMODEL_LD1_GGIO1_Ind1_subQ (&iedModel_LD1_GGIO1_Ind1_subQ) #define IEDMODEL_LD1_GGIO1_Ind1_subID (&iedModel_LD1_GGIO1_Ind1_subID) +#define IEDMODEL_LD1_GGIO1_Ind1_blkEna (&iedModel_LD1_GGIO1_Ind1_blkEna) #define IEDMODEL_LD1_GGIO1_AnIn1 (&iedModel_LD1_GGIO1_AnIn1) #define IEDMODEL_LD1_GGIO1_AnIn1_mag (&iedModel_LD1_GGIO1_AnIn1_mag) #define IEDMODEL_LD1_GGIO1_AnIn1_mag_f (&iedModel_LD1_GGIO1_AnIn1_mag_f) @@ -172,6 +183,7 @@ extern DataAttribute iedModel_LD1_GGIO1_AnIn1_subID; #define IEDMODEL_LD1_GGIO1_AnIn1_subMag_f (&iedModel_LD1_GGIO1_AnIn1_subMag_f) #define IEDMODEL_LD1_GGIO1_AnIn1_subQ (&iedModel_LD1_GGIO1_AnIn1_subQ) #define IEDMODEL_LD1_GGIO1_AnIn1_subID (&iedModel_LD1_GGIO1_AnIn1_subID) +#define IEDMODEL_LD1_GGIO1_AnIn1_blkEna (&iedModel_LD1_GGIO1_AnIn1_blkEna) #endif /* STATIC_MODEL_H_ */ diff --git a/examples/server_example_substitution/substitution_example.cid b/examples/server_example_substitution/substitution_example.cid index 32d62c33..30ac3635 100644 --- a/examples/server_example_substitution/substitution_example.cid +++ b/examples/server_example_substitution/substitution_example.cid @@ -99,6 +99,7 @@ + @@ -108,6 +109,7 @@ + diff --git a/hal/inc/hal_socket.h b/hal/inc/hal_socket.h index e52defb5..414a1a7e 100644 --- a/hal/inc/hal_socket.h +++ b/hal/inc/hal_socket.h @@ -151,8 +151,19 @@ UdpSocket_bind(UdpSocket self, const char* address, int port); PAL_API bool UdpSocket_sendTo(UdpSocket self, const char* address, int port, uint8_t* msg, int msgSize); +/** + * \brief Receive data from UDP socket (store data and (optionally) the IP address of the sender + * + * \param self UDP socket instance + * \param address (optional) buffer to store the IP address as string + * \param maxAddrSize (optional) size of the provided buffer to store the IP address + * \param msg buffer to store the UDP message data + * \param msgSize the maximum size of the message to receive + * + * \return number of received bytes or -1 in case of an error + */ PAL_API int -UdpSocket_receiveFrom(UdpSocket self, char** address, int maxAddrSize, uint8_t* msg, int msgSize); +UdpSocket_receiveFrom(UdpSocket self, char* address, int maxAddrSize, uint8_t* msg, int msgSize); PAL_API void diff --git a/hal/socket/linux/socket_linux.c b/hal/socket/linux/socket_linux.c index dc9bbaeb..3c3b284c 100644 --- a/hal/socket/linux/socket_linux.c +++ b/hal/socket/linux/socket_linux.c @@ -753,16 +753,43 @@ UdpSocket_sendTo(UdpSocket self, const char* address, int port, uint8_t* msg, in } int -UdpSocket_receiveFrom(UdpSocket self, char** address, int maxAddrSize, uint8_t* msg, int msgSize) +UdpSocket_receiveFrom(UdpSocket self, char* address, int maxAddrSize, uint8_t* msg, int msgSize) { - struct sockaddr_in remoteAddress; + struct sockaddr_storage remoteAddress; + socklen_t structSize = sizeof(struct sockaddr_storage); - int result = recvfrom(self->fd, msg, msgSize, MSG_DONTWAIT, NULL, NULL); + int result = recvfrom(self->fd, msg, msgSize, MSG_DONTWAIT, (struct sockaddr*)&remoteAddress, &structSize); if (result == -1) { if (DEBUG_SOCKET) printf("SOCKET: failed to receive UDP message (errno=%i)\n", errno); } + if (address) { + bool isIPv6; + char addrString[INET6_ADDRSTRLEN + 7]; + int port; + + if (remoteAddress.ss_family == AF_INET) { + struct sockaddr_in* ipv4Addr = (struct sockaddr_in*) &remoteAddress; + port = ntohs(ipv4Addr->sin_port); + inet_ntop(AF_INET, &(ipv4Addr->sin_addr), addrString, INET_ADDRSTRLEN); + isIPv6 = false; + } + else if (remoteAddress.ss_family == AF_INET6) { + struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*) &remoteAddress; + port = ntohs(ipv6Addr->sin6_port); + inet_ntop(AF_INET6, &(ipv6Addr->sin6_addr), addrString, INET6_ADDRSTRLEN); + isIPv6 = true; + } + else + return result ; + + if (isIPv6) + snprintf(address, maxAddrSize, "[%s]:%i", addrString, port); + else + snprintf(address, maxAddrSize, "%s:%i", addrString, port); + } + return result; } diff --git a/hal/time/unix/time.c b/hal/time/unix/time.c index 6565da33..5c1bffe7 100644 --- a/hal/time/unix/time.c +++ b/hal/time/unix/time.c @@ -1,7 +1,7 @@ /* * time.c * - * Copyright 2013, 2014 Michael Zillgith + * Copyright 2013-2021 Michael Zillgith * * This file is part of libIEC61850. * diff --git a/src/iec61850/client/client_control.c b/src/iec61850/client/client_control.c index 12b5669f..ad31648d 100644 --- a/src/iec61850/client/client_control.c +++ b/src/iec61850/client/client_control.c @@ -523,14 +523,15 @@ ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t MmsValue_update(self->ctlVal, ctlVal); - if (self->analogValue) - MmsValue_setElement(self->analogValue, 0, NULL); - self->opertime = operTime; success = true; exit_function: + + if (self->analogValue) + MmsValue_setElement(self->analogValue, 0, NULL); + return success; } @@ -624,13 +625,14 @@ ControlObjectClient_operateAsync(ControlObjectClient self, IedClientError* err, else { MmsValue_update(self->ctlVal, ctlVal); - if (self->analogValue) - MmsValue_setElement(self->analogValue, 0, NULL); - self->opertime = operTime; } exit_function: + + if (self->analogValue) + MmsValue_setElement(self->analogValue, 0, NULL); + return invokeId; } @@ -703,6 +705,8 @@ prepareSBOwParameters(ControlObjectClient self, MmsValue* ctlVal) bool ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal) { + bool retVal = true; + resetLastApplError(self); char domainId[65]; @@ -741,22 +745,30 @@ ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal) if (mmsError != MMS_ERROR_NONE) { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: select-with-value failed!\n"); - return false; + + retVal = false; + + goto exit_function; } else { if (writeResult != DATA_ACCESS_ERROR_SUCCESS) { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: select-with-value failed!\n"); - return false; + + retVal = false; + + goto exit_function; } } MmsValue_update(self->ctlVal, ctlVal); +exit_function: + if (self->analogValue) MmsValue_setElement(self->analogValue, 0, NULL); - return true; + return retVal; } static void @@ -857,12 +869,13 @@ ControlObjectClient_selectWithValueAsync(ControlObjectClient self, IedClientErro } else { MmsValue_update(self->ctlVal, ctlVal); - - if (self->analogValue) - MmsValue_setElement(self->analogValue, 0, NULL); } exit_function: + + if (self->analogValue) + MmsValue_setElement(self->analogValue, 0, NULL); + return invokeId; } diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c index a055e7af..0e2da178 100644 --- a/src/iec61850/client/ied_connection.c +++ b/src/iec61850/client/ied_connection.c @@ -473,8 +473,12 @@ handleLastApplErrorMessage(IedConnection self, MmsValue* lastApplError) self->lastApplError.ctlNum = MmsValue_toUint32(ctlNum); self->lastApplError.addCause = (ControlAddCause) MmsValue_toInt32(addCause); self->lastApplError.error = (ControlLastApplError) MmsValue_toInt32(error); + + Semaphore_wait(self->clientControlsLock); + LinkedList control = LinkedList_getNext(self->clientControls); - while (control != NULL) { + + while (control) { ControlObjectClient object = (ControlObjectClient) control->data; const char* objectRef = ControlObjectClient_getObjectReference(object); @@ -485,6 +489,8 @@ handleLastApplErrorMessage(IedConnection self, MmsValue* lastApplError) control = LinkedList_getNext(control); } + + Semaphore_post(self->clientControlsLock); } static void @@ -515,9 +521,11 @@ informationReportHandler(void* parameter, char* domainName, if (DEBUG_IED_CLIENT) printf("IED_CLIENT: RCVD CommandTermination for %s/%s\n", domainName, variableListName); + Semaphore_wait(self->clientControlsLock); + LinkedList control = LinkedList_getNext(self->clientControls); - while (control != NULL) { + while (control) { ControlObjectClient object = (ControlObjectClient) control->data; const char* objectRef = ControlObjectClient_getObjectReference(object); @@ -527,6 +535,8 @@ informationReportHandler(void* parameter, char* domainName, control = LinkedList_getNext(control); } + + Semaphore_post(self->clientControlsLock); } MmsValue_delete(value); @@ -587,6 +597,7 @@ createNewConnectionObject(TLSConfiguration tlsConfig, bool useThreads) if (self) { self->enabledReports = LinkedList_create(); self->logicalDevices = NULL; + self->clientControlsLock = Semaphore_create(1); self->clientControls = LinkedList_create(); @@ -812,6 +823,7 @@ IedConnection_destroy(IedConnection self) LinkedList_destroyStatic(self->clientControls); + Semaphore_destroy(self->clientControlsLock); Semaphore_destroy(self->outstandingCallsLock); Semaphore_destroy(self->stateMutex); Semaphore_destroy(self->reportHandlerMutex); @@ -3805,13 +3817,21 @@ IedConnection_getLastApplError(IedConnection self) void iedConnection_addControlClient(IedConnection self, ControlObjectClient control) { + Semaphore_wait(self->clientControlsLock); + LinkedList_add(self->clientControls, control); + + Semaphore_post(self->clientControlsLock); } void iedConnection_removeControlClient(IedConnection self, ControlObjectClient control) { + Semaphore_wait(self->clientControlsLock); + LinkedList_remove(self->clientControls, control); + + Semaphore_post(self->clientControlsLock); } FileDirectoryEntry diff --git a/src/iec61850/inc/iec61850_client.h b/src/iec61850/inc/iec61850_client.h index bdc55a03..c5c51818 100644 --- a/src/iec61850/inc/iec61850_client.h +++ b/src/iec61850/inc/iec61850_client.h @@ -1957,14 +1957,32 @@ ControlObjectClient_createEx(const char* objectReference, IedConnection connecti LIB61850_API void ControlObjectClient_destroy(ControlObjectClient self); +/** + * Cause of the \ref ControlObjectClient_ControlActionHandler invocation + */ typedef enum { - CONTROL_ACTION_TYPE_SELECT = 0, - CONTROL_ACTION_TYPE_OPERATE = 1, - CONTROL_ACTION_TYPE_CANCEL = 2 + CONTROL_ACTION_TYPE_SELECT = 0, /** < callback was invoked because of a select command */ + CONTROL_ACTION_TYPE_OPERATE = 1, /** < callback was invoked because of an operate command */ + CONTROL_ACTION_TYPE_CANCEL = 2 /** < callback was invoked because of a cancel command */ } ControlActionType; - +/** + * \brief A callback handler that is invoked when a command termination message is received. + * + * This callback is invoked whenever a CommandTermination+ or CommandTermination- message is received. + * To distinguish between a CommandTermination+ and CommandTermination- please use the + * ControlObjectClient_getLastApplError function. + * + * NOTE: Do not call \ref ControlObjectClient_destroy inside of this callback! Doing so will cause a dead-lock. + * + * \param invokeId invoke ID of the command sent by the client + * \param parameter the user parameter that is passed to the callback function + * \param err the error code when an error occurred, or IED_ERROR_OK + * \param type control action type that caused the callback + * \param success true, when the command was successful, false otherwise + * + */ typedef void (*ControlObjectClient_ControlActionHandler) (uint32_t invokeId, void* parameter, IedClientError err, ControlActionType type, bool success); @@ -2196,7 +2214,7 @@ ControlObjectClient_setOrigin(ControlObjectClient self, const char* orIdent, int * NOTE: Some non-standard compliant servers may require this to accept oper/cancel requests * * \param self the ControlObjectClient instance - * \param useContantT enable this behaviour with true, disable with false + * \param useContantT enable this behavior with true, disable with false */ LIB61850_API void ControlObjectClient_useConstantT(ControlObjectClient self, bool useConstantT); @@ -2233,13 +2251,15 @@ ControlObjectClient_setSynchroCheck(ControlObjectClient self, bool value); /** - * \brief Private a callback handler that is invoked when a command termination message is received. + * \brief A callback handler that is invoked when a command termination message is received. * * This callback is invoked whenever a CommandTermination+ or CommandTermination- message is received. * To distinguish between a CommandTermination+ and CommandTermination- please use the * ControlObjectClient_getLastApplError function. * - * \param parameter the user paramter that is passed to the callback function + * NOTE: Do not call \ref ControlObjectClient_destroy inside of this callback! Doing so will cause a dead-lock. + * + * \param parameter the user parameter that is passed to the callback function * \param controlClient the ControlObjectClient instance */ typedef void (*CommandTerminationHandler) (void* parameter, ControlObjectClient controlClient); diff --git a/src/iec61850/inc_private/ied_connection_private.h b/src/iec61850/inc_private/ied_connection_private.h index 3b061b5b..5b7200ed 100644 --- a/src/iec61850/inc_private/ied_connection_private.h +++ b/src/iec61850/inc_private/ied_connection_private.h @@ -56,7 +56,10 @@ struct sIedConnection IedConnectionState state; LinkedList enabledReports; LinkedList logicalDevices; + + Semaphore clientControlsLock; LinkedList clientControls; + LastApplError lastApplError; Semaphore stateMutex; diff --git a/src/iec61850/inc_private/ied_server_private.h b/src/iec61850/inc_private/ied_server_private.h index 3d6aa3fd..6b1aad8a 100644 --- a/src/iec61850/inc_private/ied_server_private.h +++ b/src/iec61850/inc_private/ied_server_private.h @@ -32,6 +32,7 @@ #define ALLOW_WRITE_ACCESS_SP 4 #define ALLOW_WRITE_ACCESS_SV 8 #define ALLOW_WRITE_ACCESS_SE 16 +#define ALLOW_WRITE_ACCESS_BL 32 struct sIedServer { diff --git a/src/iec61850/inc_private/reporting.h b/src/iec61850/inc_private/reporting.h index b6d0bb75..f57abdf1 100644 --- a/src/iec61850/inc_private/reporting.h +++ b/src/iec61850/inc_private/reporting.h @@ -106,6 +106,8 @@ typedef struct { ReportBuffer* reportBuffer; MmsValue* timeOfEntry; + ReportControlBlock* rcb; + IedServer server; } ReportControl; diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index 86497ccf..7f74d3fc 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -64,7 +64,7 @@ typedef struct EditSettingGroupConfirmationHandler editSgConfirmedHandler; void* editSgConfirmedHandlerParameter; - ClientConnection editingClient; + MmsServerConnection editingClient; uint64_t reservationTimeout; } SettingGroup; @@ -587,7 +587,7 @@ unselectAllSettingGroups(MmsMapping* self, MmsServerConnection serverCon) while (settingGroupElement != NULL) { SettingGroup* settingGroup = (SettingGroup*) LinkedList_getData(settingGroupElement); - if (settingGroup->editingClient == (ClientConnection) serverCon) + if (settingGroup->editingClient == serverCon) unselectEditSettingGroup(settingGroup); settingGroupElement = LinkedList_getNext(settingGroupElement); @@ -2100,45 +2100,9 @@ isReportControlBlock(char* separator) #endif /* (CONFIG_IEC61850_REPORT_SERVICE == 1) */ static bool -isFunctionalConstraintCF(char* separator) +isFunctionalConstraint(const char* fcStr, char* separator) { - if (strncmp(separator + 1, "CF", 2) == 0) - return true; - else - return false; -} - -static bool -isFunctionalConstraintDC(char* separator) -{ - if (strncmp(separator + 1, "DC", 2) == 0) - return true; - else - return false; -} - -static bool -isFunctionalConstraintSP(char* separator) -{ - if (strncmp(separator + 1, "SP", 2) == 0) - return true; - else - return false; -} - -static bool -isFunctionalConstraintSV(char* separator) -{ - if (strncmp(separator + 1, "SV", 2) == 0) - return true; - else - return false; -} - -static bool -isFunctionalConstraintSE(char* separator) -{ - if (strncmp(separator + 1, "SE", 2) == 0) + if (strncmp(separator + 1, fcStr, 2) == 0) return true; else return false; @@ -2445,16 +2409,18 @@ checkIfValueBelongsToModelNode(DataAttribute* dataAttribute, MmsValue* value, Mm static FunctionalConstraint getFunctionalConstraintForWritableNode(char* separator) { - if (isFunctionalConstraintCF(separator)) + if (isFunctionalConstraint("CF", separator)) return IEC61850_FC_CF; - if (isFunctionalConstraintDC(separator)) + if (isFunctionalConstraint("DC", separator)) return IEC61850_FC_DC; - if (isFunctionalConstraintSP(separator)) + if (isFunctionalConstraint("SP", separator)) return IEC61850_FC_SP; - if (isFunctionalConstraintSV(separator)) + if (isFunctionalConstraint("SV", separator)) return IEC61850_FC_SV; - if (isFunctionalConstraintSE(separator)) + if (isFunctionalConstraint("SE", separator)) return IEC61850_FC_SE; + if (isFunctionalConstraint("BL", separator)) + return IEC61850_FC_BL; return IEC61850_FC_NONE; } @@ -2497,6 +2463,13 @@ getAccessPolicyForFC(MmsMapping* self, FunctionalConstraint fc) return ACCESS_POLICY_DENY; } + if (fc == IEC61850_FC_BL) { + if (self->iedServer->writeAccessPolicies & ALLOW_WRITE_ACCESS_BL) + return ACCESS_POLICY_ALLOW; + else + return ACCESS_POLICY_DENY; + } + return ACCESS_POLICY_DENY; } @@ -2622,9 +2595,12 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, if ((val > 0) && (val <= sg->sgcb->numOfSGs)) { if (val != sg->sgcb->actSG) { - if (sg->actSgChangedHandler != NULL) { + if (sg->actSgChangedHandler) { + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + if (sg->actSgChangedHandler(sg->actSgChangedHandlerParameter, sg->sgcb, - (uint8_t) val, (ClientConnection) connection)) + (uint8_t) val, clientConnection)) { sg->sgcb->actSG = val; @@ -2634,7 +2610,6 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, MmsValue_setUint8(actSg, sg->sgcb->actSG); MmsValue_setUtcTimeMs(lActTm, Hal_getTimeInMs()); } - else retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; } @@ -2661,7 +2636,7 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, if (sg != NULL) { uint32_t val = MmsValue_toUint32(value); - if ((sg->editingClient != NULL) && ( sg->editingClient != (ClientConnection) connection)) { + if ((sg->editingClient != NULL) && ( sg->editingClient != connection)) { /* Edit SG was set by other client */ retVal = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; } @@ -2674,13 +2649,15 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, if ((val > 0) && (val <= sg->sgcb->numOfSGs)) { - if (sg->editSgChangedHandler != NULL) { + if (sg->editSgChangedHandler) { + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); if (sg->editSgChangedHandler(sg->editSgChangedHandlerParameter, sg->sgcb, - (uint8_t) val, (ClientConnection) connection)) + (uint8_t) val, clientConnection)) { sg->sgcb->editSG = val; - sg->editingClient = (ClientConnection) connection; + sg->editingClient = connection; sg->reservationTimeout = Hal_getTimeInMs() + (sg->sgcb->resvTms * 1000); @@ -2736,8 +2713,8 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, if (val == true) { if (sg->sgcb->editSG != 0) { - if (sg->editingClient == (ClientConnection) connection) { - if (sg->editSgConfirmedHandler != NULL) { + if (sg->editingClient == connection) { + if (sg->editSgConfirmedHandler) { sg->editSgConfirmedHandler(sg->editSgConfirmedHandlerParameter, sg->sgcb, sg->sgcb->editSG); @@ -2784,7 +2761,7 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, SettingGroup* sg = getSettingGroupByMmsDomain(self, domain); if (sg != NULL) { - if (sg->editingClient != (ClientConnection) connection) + if (sg->editingClient != connection) return DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; } else @@ -2811,7 +2788,7 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, printf("IED_SERVER: write to %s policy:%i\n", variableId, nodeAccessPolicy); #if (CONFIG_IEC61850_SETTING_GROUPS == 1) - if (isFunctionalConstraintSE(separator)) { + if (isFunctionalConstraint("SE", separator)) { SettingGroup* sg = getSettingGroupByMmsDomain(self, domain); if (sg != NULL) { @@ -3189,7 +3166,7 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS #if (CONFIG_IEC61850_SETTING_GROUPS == 1) if (separator) { - if (isFunctionalConstraintSE(separator)) { + if (isFunctionalConstraint("SE", separator)) { SettingGroup* sg = getSettingGroupByMmsDomain(self, domain); if (sg != NULL) { diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c index 74398794..637f0503 100644 --- a/src/iec61850/server/mms_mapping/reporting.c +++ b/src/iec61850/server/mms_mapping/reporting.c @@ -40,7 +40,7 @@ #include /* if not explicitly set by client "ResvTms" will be set to this value */ -#define RESV_TMS_IMPLICIT_VALUE 30 +#define RESV_TMS_IMPLICIT_VALUE 10 #ifndef DEBUG_IED_SERVER #define DEBUG_IED_SERVER 0 @@ -1345,6 +1345,8 @@ Reporting_createMmsBufferedRCBs(MmsMapping* self, MmsDomain* domain, rc->name = StringUtils_createString(3, logicalNode->name, "$BR$", reportControlBlock->name); + rc->rcb = reportControlBlock; + namedVariable->typeSpec.structure.elements[currentReport] = createBufferedReportControlBlock(reportControlBlock, rc); @@ -1385,6 +1387,8 @@ Reporting_createMmsUnbufferedRCBs(MmsMapping* self, MmsDomain* domain, rc->name = StringUtils_createString(3, logicalNode->name, "$RP$", reportControlBlock->name); + rc->rcb = reportControlBlock; + namedVariable->typeSpec.structure.elements[currentReport] = createUnbufferedReportControlBlock(reportControlBlock, rc); @@ -1472,8 +1476,19 @@ updateOwner(ReportControl* rc, MmsServerConnection connection) } } else { - uint8_t emptyAddr[1]; - MmsValue_setOctetString(owner, emptyAddr, 0); + if (rc->resvTms != -1) { + uint8_t emptyAddr[1]; + MmsValue_setOctetString(owner, emptyAddr, 0); + } + else { + /* Set to pre-configured owner */ + if (rc->rcb->clientReservation[0] == 4) { + MmsValue_setOctetString(owner, rc->rcb->clientReservation + 1, 4); + } + else if (rc->rcb->clientReservation[0] == 6) { + MmsValue_setOctetString(owner, rc->rcb->clientReservation + 1, 16); + } + } } } } @@ -1539,9 +1554,14 @@ static void checkReservationTimeout(MmsMapping* self, ReportControl* rc) { if (rc->enabled == false) { - if (rc->resvTms > 0) { + if (rc->reservationTimeout > 0) { if (Hal_getTimeInMs() > rc->reservationTimeout) { - rc->resvTms = 0; + + if (rc->resvTms != -1) + rc->resvTms = 0; + + if (DEBUG_IED_SERVER) + printf("IED_SERVER: reservation timeout expired for %s.%s\n", rc->parentLN->name, rc->name); #if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1) MmsValue* resvTmsVal = ReportControl_getRCBValue(rc, "ResvTms"); @@ -1646,6 +1666,10 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme rc->reserved = true; rc->clientConnection = connection; } + else { + if (DEBUG_IED_SERVER) + printf("IED_SERVER: client IP not matching with pre-assigned owner\n"); + } #else rc->reserved = true; rc->clientConnection = connection; @@ -1681,6 +1705,18 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme goto exit_function; } +#if (CONFIG_IEC61850_RCB_ALLOW_ONLY_PRECONFIGURED_CLIENT == 1) + if (isIpAddressMatchingWithOwner(rc, MmsServerConnection_getClientAddress(connection)) == false) { + retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + + if (DEBUG_IED_SERVER) + printf("IED_SERVER: client IP not matching with pre-assigned owner --> write access denied!\n"); + + goto exit_function; + } +#endif + + if (strcmp(elementName, "RptEna") == 0) { if (value->value.boolean == true) { @@ -1696,8 +1732,7 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme if (updateReportDataset(self, rc, NULL, connection)) { - if (rc->resvTms != -1) - updateOwner(rc, connection); + updateOwner(rc, connection); MmsValue* rptEna = ReportControl_getRCBValue(rc, "RptEna"); @@ -1998,6 +2033,9 @@ exit_function: reserveRcb(rc, connection); } + else if (rc->resvTms == -1) { + reserveRcb(rc, connection); + } } #if (CONFIG_IEC61850_SERVICE_TRACKING == 1) diff --git a/src/sampled_values/sv_subscriber.c b/src/sampled_values/sv_subscriber.c index 950e803e..106430e0 100644 --- a/src/sampled_values/sv_subscriber.c +++ b/src/sampled_values/sv_subscriber.c @@ -156,17 +156,30 @@ static void* svReceiverLoop(void* threadParameter) { SVReceiver self = (SVReceiver) threadParameter; + EthernetHandleSet handleSet = EthernetHandleSet_new(); + EthernetHandleSet_addSocket(handleSet, self->ethSocket); self->stopped = false; while (self->running) { + switch (EthernetHandleSet_waitReady(handleSet, 100)) + { + case -1: + if (DEBUG_SV_SUBSCRIBER) + printf("SV_SUBSCRIBER: EhtnernetHandleSet_waitReady() failure\n"); + break; + case 0: + break; + default: + SVReceiver_tick(self); + } - if (SVReceiver_tick(self) == false) - Thread_sleep(1); } self->stopped = true; + EthernetHandleSet_destroy(handleSet); + return NULL; }