pull/309/head
Michael Zillgith 5 years ago
commit 5f2e14f712

@ -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() */

@ -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;

@ -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_ */

@ -99,6 +99,7 @@
<DA bType="BOOLEAN" fc="SV" name="subVal"/>
<DA bType="Quality" fc="SV" name="subQ"/>
<DA bType="VisString64" fc="SV" name="subID"/>
<DA bType="BOOLEAN" fc="BL" name="blkEna"/>
</DOType>
<DOType cdc="MV" id="MV">
<DA bType="Struct" dchg="true" dupd="false" fc="MX" name="mag" type="AnalogueValue"/>
@ -108,6 +109,7 @@
<DA bType="Struct" fc="SV" name="subMag" type="AnalogueValue"/>
<DA bType="Quality" fc="SV" name="subQ"/>
<DA bType="VisString64" fc="SV" name="subID"/>
<DA bType="BOOLEAN" fc="BL" name="blkEna"/>
</DOType>
<DAType id="AnalogueValue">
<BDA bType="FLOAT32" name="f"/>

@ -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

@ -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;
}

@ -1,7 +1,7 @@
/*
* time.c
*
* Copyright 2013, 2014 Michael Zillgith
* Copyright 2013-2021 Michael Zillgith
*
* This file is part of libIEC61850.
*

@ -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;
}

@ -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

@ -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);

@ -56,7 +56,10 @@ struct sIedConnection
IedConnectionState state;
LinkedList enabledReports;
LinkedList logicalDevices;
Semaphore clientControlsLock;
LinkedList clientControls;
LastApplError lastApplError;
Semaphore stateMutex;

@ -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
{

@ -106,6 +106,8 @@ typedef struct {
ReportBuffer* reportBuffer;
MmsValue* timeOfEntry;
ReportControlBlock* rcb;
IedServer server;
} ReportControl;

@ -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) {

@ -40,7 +40,7 @@
#include <string.h>
/* 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)

@ -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;
}

Loading…
Cancel
Save