From 74d78d061887247d3507a41b44f4139308d7673d Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 19 Jul 2019 09:25:10 +0200 Subject: [PATCH 1/6] updated server substitution example --- .../server_example_substitution.c | 65 +++++++++++-------- src/iec61850/inc/iec61850_client.h | 2 +- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/examples/server_example_substitution/server_example_substitution.c b/examples/server_example_substitution/server_example_substitution.c index 043280a3..b4052254 100644 --- a/examples/server_example_substitution/server_example_substitution.c +++ b/examples/server_example_substitution/server_example_substitution.c @@ -25,6 +25,10 @@ static IedServer iedServer = NULL; static bool subsAnIn1 = false; static bool subsInd1 = false; +static float an1 = 0.f; +static uint64_t timestamp = 0; +static bool ind1 = true; + void sigint_handler(int signalId) { @@ -41,6 +45,28 @@ connectionHandler (IedServer self, ClientConnection connection, bool connected, printf("Connection closed\n"); } +static void +updateProcessValues() +{ + Timestamp iecTimestamp; + + Timestamp_clearFlags(&iecTimestamp); + Timestamp_setTimeInMilliseconds(&iecTimestamp, timestamp); + Timestamp_setLeapSecondKnown(&iecTimestamp, true); + + if (subsAnIn1 == 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) { + 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); + } +} + static MmsDataAccessError writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnection connection, void* parameter) { @@ -64,7 +90,11 @@ writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnect IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_subMag_f)); } else { + IedServer_updateVisibleStringAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_subID, ""); + subsAnIn1 = false; + + updateProcessValues(); } } @@ -105,7 +135,11 @@ writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnect IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_Ind1_subVal)); } else { + IedServer_updateVisibleStringAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_Ind1_subID, ""); + subsInd1 = false; + + updateProcessValues(); } } else if (dataAttribute == IEDMODEL_LD1_GGIO1_Ind1_subVal) { @@ -135,9 +169,6 @@ main(int argc, char** argv) { printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString()); - - - /* Create a new IEC 61850 server instance */ iedServer = IedServer_create(&iedModel); @@ -167,44 +198,22 @@ main(int argc, char** argv) signal(SIGINT, sigint_handler); float t = 0.f; - bool ind1 = true; while (running) { - uint64_t timestamp = Hal_getTimeInMs(); + timestamp = Hal_getTimeInMs(); t += 0.1f; - float an1 = sinf(t); + an1 = sinf(t); if (ind1) ind1 = false; else ind1 = true; - Timestamp iecTimestamp; - - Timestamp_clearFlags(&iecTimestamp); - Timestamp_setTimeInMilliseconds(&iecTimestamp, timestamp); - Timestamp_setLeapSecondKnown(&iecTimestamp, true); - - - /* toggle clock-not-synchronized flag in timestamp */ - if (((int) t % 2) == 0) - Timestamp_setClockNotSynchronized(&iecTimestamp, true); - IedServer_lockDataModel(iedServer); - if (subsAnIn1 == 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) { - 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); - } + updateProcessValues(); IedServer_unlockDataModel(iedServer); diff --git a/src/iec61850/inc/iec61850_client.h b/src/iec61850/inc/iec61850_client.h index 340ee381..45cae7f5 100644 --- a/src/iec61850/inc/iec61850_client.h +++ b/src/iec61850/inc/iec61850_client.h @@ -1102,7 +1102,7 @@ typedef enum { /** the element is included due to a general interrogation by the client */ IEC61850_REASON_GI = 5, - /** the reason for inclusion is unknown */ + /** the reason for inclusion is unknown (e.g. report is not configured to include reason-for-inclusion) */ IEC61850_REASON_UNKNOWN = 6 } ReasonForInclusion; From 46d7f6ff2dde6a580fda0e9d0574bbc1f125936c Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sat, 27 Jul 2019 15:30:49 +0200 Subject: [PATCH 2/6] - SV publisher: make VLAN tagging optional --- src/sampled_values/sv_publisher.c | 30 +++++++++++++++++++----------- src/sampled_values/sv_publisher.h | 14 ++++++++++++++ 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/sampled_values/sv_publisher.c b/src/sampled_values/sv_publisher.c index 02dfee64..90d2de7b 100644 --- a/src/sampled_values/sv_publisher.c +++ b/src/sampled_values/sv_publisher.c @@ -85,7 +85,7 @@ struct sSVPublisher { static bool -preparePacketBuffer(SVPublisher self, CommParameters* parameters, const char* interfaceId) +preparePacketBuffer(SVPublisher self, CommParameters* parameters, const char* interfaceId, bool useVlanTags) { uint8_t defaultDstAddr[] = CONFIG_SV_DEFAULT_DST_ADDRESS; @@ -134,17 +134,19 @@ preparePacketBuffer(SVPublisher self, CommParameters* parameters, const char* in int bufPos = 12; - /* Priority tag - IEEE 802.1Q */ - self->buffer[bufPos++] = 0x81; - self->buffer[bufPos++] = 0x00; + if (useVlanTags) { + /* Priority tag - IEEE 802.1Q */ + self->buffer[bufPos++] = 0x81; + self->buffer[bufPos++] = 0x00; - uint8_t tci1 = priority << 5; - tci1 += vlanId / 256; + uint8_t tci1 = priority << 5; + tci1 += vlanId / 256; - uint8_t tci2 = vlanId % 256; + uint8_t tci2 = vlanId % 256; - self->buffer[bufPos++] = tci1; /* Priority + VLAN-ID */ - self->buffer[bufPos++] = tci2; /* VLAN-ID */ + self->buffer[bufPos++] = tci1; /* Priority + VLAN-ID */ + self->buffer[bufPos++] = tci2; /* VLAN-ID */ + } /* EtherType Sampled Values */ self->buffer[bufPos++] = 0x88; @@ -293,14 +295,14 @@ encodeUtcTime(uint64_t timeval, uint8_t* buffer, int bufPos) } SVPublisher -SVPublisher_create(CommParameters* parameters, const char* interfaceId) +SVPublisher_createEx(CommParameters* parameters, const char* interfaceId, bool useVlanTag) { SVPublisher self = (SVPublisher) GLOBAL_CALLOC(1, sizeof(struct sSVPublisher)); if (self) { self->asduList = NULL; - if (preparePacketBuffer(self, parameters, interfaceId) == false) { + if (preparePacketBuffer(self, parameters, interfaceId, useVlanTag) == false) { GLOBAL_FREEMEM(self); self = NULL; } @@ -310,6 +312,12 @@ SVPublisher_create(CommParameters* parameters, const char* interfaceId) return self; } +SVPublisher +SVPublisher_create(CommParameters* parameters, const char* interfaceId) +{ + return SVPublisher_createEx(parameters, interfaceId, true); +} + SVPublisher_ASDU SVPublisher_addASDU(SVPublisher self, const char* svID, const char* datset, uint32_t confRev) { diff --git a/src/sampled_values/sv_publisher.h b/src/sampled_values/sv_publisher.h index dae01d27..3c59c1b4 100644 --- a/src/sampled_values/sv_publisher.h +++ b/src/sampled_values/sv_publisher.h @@ -70,6 +70,9 @@ typedef struct sSVPublisher_ASDU* SVPublisher_ASDU; /** * \brief Create a new IEC61850-9-2 Sampled Values publisher. * + * NOTE: VLAN tagging is enabled when calling this constructor. To disable VLAN tagging + * use \ref SVPublisher_createEx instead. + * * \param[in] interfaceId the name of the interface over which the SV publisher should send SV packets. * \param[in] parameters optional parameters for setting VLAN options and destination MAC address. Use NULL for default values. * \return the new SV publisher instance. @@ -77,6 +80,17 @@ typedef struct sSVPublisher_ASDU* SVPublisher_ASDU; LIB61850_API SVPublisher SVPublisher_create(CommParameters* parameters, const char* interfaceId); +/** + * \brief Create a new IEC61850-9-2 Sampled Values publisher. + * + * \param[in] interfaceId the name of the interface over which the SV publisher should send SV packets. + * \param[in] parameters optional parameters for setting VLAN options and destination MAC address. Use NULL for default values. + * \param[in] useVlanTags enable(true)/disable(false) VLAN tagging + * \return the new SV publisher instance. + */ +LIB61850_API SVPublisher +SVPublisher_createEx(CommParameters* parameters, const char* interfaceId, bool useVlanTag); + /** * \brief Create an Application Service Data Unit (ASDU) and add it to an existing Sampled Values publisher. * From 6b2404e80bcf0a6946079e8e6460bfc3371327e8 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sat, 27 Jul 2019 13:42:18 +0200 Subject: [PATCH 3/6] - GOOSE: option to enable/disable VLAN tagging - IED Server: Allow setting different interfaces for different GOOSE publishers --- .../server_example_goose.c | 12 ++++++ src/goose/goose_publisher.c | 35 +++++++++------- src/goose/goose_publisher.h | 3 ++ src/iec61850/inc/iec61850_server.h | 38 +++++++++++++++++ src/iec61850/inc_private/mms_goose.h | 11 ++++- src/iec61850/inc_private/mms_mapping.h | 6 +++ src/iec61850/server/impl/ied_server.c | 15 +++++++ src/iec61850/server/mms_mapping/mms_goose.c | 41 +++++++++++++++++-- src/iec61850/server/mms_mapping/mms_mapping.c | 38 +++++++++++++++++ 9 files changed, 179 insertions(+), 20 deletions(-) diff --git a/examples/server_example_goose/server_example_goose.c b/examples/server_example_goose/server_example_goose.c index 7f0681d9..84e36b0d 100644 --- a/examples/server_example_goose/server_example_goose.c +++ b/examples/server_example_goose/server_example_goose.c @@ -59,9 +59,21 @@ int main(int argc, char** argv) { char* ethernetIfcID = argv[1]; printf("Using GOOSE interface: %s\n", ethernetIfcID); + + /* set GOOSE interface for all GOOSE publishers (GCBs) */ IedServer_setGooseInterfaceId(iedServer, ethernetIfcID); } + if (argc > 2) { + char* ethernetIfcID = argv[2]; + + printf("Using GOOSE interface for GenericIO/LLN0.gcbAnalogValues: %s\n", ethernetIfcID); + + /* set GOOSE interface for a particular GOOSE publisher (GCB) */ + IedServer_setGooseInterfaceIdEx(iedServer, IEDMODEL_GenericIO_LLN0, "gcbAnalogValues", ethernetIfcID); + } + + /* MMS server will be instructed to start listening to client connections. */ IedServer_start(iedServer, 102); diff --git a/src/goose/goose_publisher.c b/src/goose/goose_publisher.c index 7f418b02..97732268 100644 --- a/src/goose/goose_publisher.c +++ b/src/goose/goose_publisher.c @@ -36,7 +36,7 @@ #define GOOSE_MAX_MESSAGE_SIZE 1518 static void -prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char* interfaceID); +prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char* interfaceID, bool useVlanTags); struct sGoosePublisher { uint8_t* buffer; @@ -63,13 +63,12 @@ struct sGoosePublisher { MmsValue* timestamp; /* time when stNum is increased */ }; - GoosePublisher -GoosePublisher_create(CommParameters* parameters, const char* interfaceID) +GoosePublisher_createEx(CommParameters* parameters, const char* interfaceID, bool useVlanTag) { GoosePublisher self = (GoosePublisher) GLOBAL_CALLOC(1, sizeof(struct sGoosePublisher)); - prepareGooseBuffer(self, parameters, interfaceID); + prepareGooseBuffer(self, parameters, interfaceID, useVlanTag); self->timestamp = MmsValue_newUtcTimeByMsTime(Hal_getTimeInMs()); @@ -78,6 +77,12 @@ GoosePublisher_create(CommParameters* parameters, const char* interfaceID) return self; } +GoosePublisher +GoosePublisher_create(CommParameters* parameters, const char* interfaceID) +{ + return GoosePublisher_createEx(parameters, interfaceID, true); +} + void GoosePublisher_destroy(GoosePublisher self) { @@ -160,7 +165,7 @@ GoosePublisher_setTimeAllowedToLive(GoosePublisher self, uint32_t timeAllowedToL } static void -prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char* interfaceID) +prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char* interfaceID, bool useVlanTags) { uint8_t srcAddr[6]; @@ -201,19 +206,19 @@ prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char* int bufPos = 12; -#if 1 - /* Priority tag - IEEE 802.1Q */ - self->buffer[bufPos++] = 0x81; - self->buffer[bufPos++] = 0x00; + if (useVlanTags) { + /* Priority tag - IEEE 802.1Q */ + self->buffer[bufPos++] = 0x81; + self->buffer[bufPos++] = 0x00; - uint8_t tci1 = priority << 5; - tci1 += vlanId / 256; + uint8_t tci1 = priority << 5; + tci1 += vlanId / 256; - uint8_t tci2 = vlanId % 256; + uint8_t tci2 = vlanId % 256; - self->buffer[bufPos++] = tci1; /* Priority + VLAN-ID */ - self->buffer[bufPos++] = tci2; /* VLAN-ID */ -#endif + self->buffer[bufPos++] = tci1; /* Priority + VLAN-ID */ + self->buffer[bufPos++] = tci2; /* VLAN-ID */ + } /* EtherType GOOSE */ self->buffer[bufPos++] = 0x88; diff --git a/src/goose/goose_publisher.h b/src/goose/goose_publisher.h index 9d250f8d..412a4e7b 100644 --- a/src/goose/goose_publisher.h +++ b/src/goose/goose_publisher.h @@ -49,6 +49,9 @@ typedef struct sGoosePublisher* GoosePublisher; LIB61850_API GoosePublisher GoosePublisher_create(CommParameters* parameters, const char* interfaceID); +LIB61850_API GoosePublisher +GoosePublisher_createEx(CommParameters* parameters, const char* interfaceID, bool useVlanTag); + LIB61850_API void GoosePublisher_destroy(GoosePublisher self); diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index 6bf9b4f5..c922eb88 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -459,6 +459,8 @@ IedServer_getMmsServer(IedServer self); * then configured GOOSE control blocks keep inactive until a MMS client enables * them by writing to the GOOSE control block. * + * Note: This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set. + * * \param self the instance of IedServer to operate on. */ LIB61850_API void @@ -470,6 +472,8 @@ IedServer_enableGoosePublishing(IedServer self); * This will set the GoEna attribute of all configured GOOSE control blocks * to false. This will stop GOOSE transmission. * + * Note: This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set. + * * \param self the instance of IedServer to operate on. */ LIB61850_API void @@ -482,12 +486,46 @@ IedServer_disableGoosePublishing(IedServer self); * default interface ID from stack_config.h is used. Note the interface ID is operating system * specific! * + * Note: This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set. + * * \param self the instance of IedServer to operate on. * \param interfaceId the ID of the ethernet interface to be used for GOOSE publishing */ LIB61850_API void IedServer_setGooseInterfaceId(IedServer self, const char* interfaceId); +/** + * \brief Set the Ethernet interface to be used by GOOSE publishing + * + * This function can be used to set the GOOSE interface ID forG all CBs (parameter ln = NULL) or for + * a specific GCB specified by the logical node instance and the GCB name. + * + * Note: This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set. + * + * \param self the instance of IedServer to operate on. + * \param ln the logical node that contains the GCB or NULL to enable/disable VLAN tagging for all GCBs + * \param gcbName the name (not object reference!) of the GCB + * \param interfaceId the ID of the ethernet interface to be used for GOOSE publishing + */ +LIB61850_API void +IedServer_setGooseInterfaceIdEx(IedServer self, LogicalNode* ln, const char* gcbName, const char* interfaceId); + +/** + * \brief Enable/disable the use of VLAN tags in GOOSE messages + * + * This function can be used to enable/disable VLAN tagging for all GCBs (parameter ln = NULL) or for + * a specific GCB specified by the logical node instance and the GCB name. + * + * Note: This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set. + * + * \param self the instance of IedServer to operate on + * \param ln the logical node that contains the GCB or NULL to enable/disable VLAN tagging for all GCBs + * \param gcbName the name (not object reference!) of the GCB + * \param useVlanTag true to enable VLAN tagging, false otherwise + */ +LIB61850_API void +IedServer_useGooseVlanTag(IedServer self, LogicalNode* ln, const char* gcbName, bool useVlanTag); + /**@}*/ /** diff --git a/src/iec61850/inc_private/mms_goose.h b/src/iec61850/inc_private/mms_goose.h index c26be24f..0b1a22d5 100644 --- a/src/iec61850/inc_private/mms_goose.h +++ b/src/iec61850/inc_private/mms_goose.h @@ -1,7 +1,7 @@ /* * mms_goose.h * - * Copyright 2013-2018 Michael Zillgith + * Copyright 2013-2019 Michael Zillgith * * This file is part of libIEC61850. * @@ -35,6 +35,15 @@ MmsGooseControlBlock_destroy(MmsGooseControlBlock self); LIB61850_INTERNAL MmsDomain* MmsGooseControlBlock_getDomain(MmsGooseControlBlock self); +LIB61850_INTERNAL void +MmsGooseControlBlock_useGooseVlanTag(MmsGooseControlBlock self, bool useVlanTag); + +LIB61850_INTERNAL void +MmsGooseControlBlock_setGooseInterfaceId(MmsGooseControlBlock self, const char* interfaceId); + +LIB61850_INTERNAL LogicalNode* +MmsGooseControlBlock_getLogicalNode(MmsGooseControlBlock self); + LIB61850_INTERNAL char* MmsGooseControlBlock_getLogicalNodeName(MmsGooseControlBlock self); diff --git a/src/iec61850/inc_private/mms_mapping.h b/src/iec61850/inc_private/mms_mapping.h index 7c768475..4590eb34 100644 --- a/src/iec61850/inc_private/mms_mapping.h +++ b/src/iec61850/inc_private/mms_mapping.h @@ -106,6 +106,12 @@ MmsMapping_enableGoosePublishing(MmsMapping* self); LIB61850_INTERNAL void MmsMapping_disableGoosePublishing(MmsMapping* self); +LIB61850_INTERNAL void +MmsMapping_useGooseVlanTag(MmsMapping* self, LogicalNode* ln, const char* gcbName, bool useVlanTag); + +LIB61850_INTERNAL void +MmsMapping_setGooseInterfaceId(MmsMapping* self, LogicalNode* ln, const char* gcbName, const char* interfaceId); + LIB61850_INTERNAL void MmsMapping_addControlObject(MmsMapping* self, ControlObject* controlObject); diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index 96c8a244..b12c71d5 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -1287,6 +1287,21 @@ IedServer_updateQuality(IedServer self, DataAttribute* dataAttribute, Quality qu } +void +IedServer_useGooseVlanTag(IedServer self, LogicalNode* ln, const char* gcbName, bool useVlanTag) +{ +#if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) + MmsMapping_useGooseVlanTag(self->mmsMapping, ln, gcbName, useVlanTag); +#endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */ +} + +void +IedServer_setGooseInterfaceIdEx(IedServer self, LogicalNode* ln, const char* gcbName, const char* interfaceId) +{ +#if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) + MmsMapping_setGooseInterfaceId(self->mmsMapping, ln, gcbName, interfaceId); +#endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */ +} void IedServer_enableGoosePublishing(IedServer self) diff --git a/src/iec61850/server/mms_mapping/mms_goose.c b/src/iec61850/server/mms_mapping/mms_goose.c index b6c2f517..91d8748d 100644 --- a/src/iec61850/server/mms_mapping/mms_goose.c +++ b/src/iec61850/server/mms_mapping/mms_goose.c @@ -39,7 +39,9 @@ struct sMmsGooseControlBlock { char* name; - bool goEna; + int goEna:1; + int isDynamicDataSet:1; + int useVlanTag:1; char* dstAddress; @@ -50,7 +52,6 @@ struct sMmsGooseControlBlock { GoosePublisher publisher; DataSet* dataSet; - bool isDynamicDataSet; LinkedList dataSetValues; uint64_t nextPublishTime; @@ -68,6 +69,8 @@ struct sMmsGooseControlBlock { char* goCBRef; char* goId; char* dataSetRef; + + char* gooseInterfaceId; }; MmsGooseControlBlock @@ -75,9 +78,12 @@ MmsGooseControlBlock_create() { MmsGooseControlBlock self = (MmsGooseControlBlock) GLOBAL_CALLOC(1, sizeof(struct sMmsGooseControlBlock)); + if (self) { + self->useVlanTag = true; #if (CONFIG_MMS_THREADLESS_STACK != 1) - self->publisherMutex = Semaphore_create(1); + self->publisherMutex = Semaphore_create(1); #endif + } return self; } @@ -112,11 +118,29 @@ MmsGooseControlBlock_destroy(MmsGooseControlBlock self) } } + if (self->gooseInterfaceId != NULL) + GLOBAL_FREEMEM(self->gooseInterfaceId); + MmsValue_delete(self->mmsValue); GLOBAL_FREEMEM(self); } +void +MmsGooseControlBlock_useGooseVlanTag(MmsGooseControlBlock self, bool useVlanTag) +{ + self->useVlanTag = useVlanTag; +} + +void +MmsGooseControlBlock_setGooseInterfaceId(MmsGooseControlBlock self, const char* interfaceId) +{ + if (self->gooseInterfaceId != NULL) + GLOBAL_FREEMEM(self->gooseInterfaceId); + + self->gooseInterfaceId = StringUtils_copyString(interfaceId); +} + MmsDomain* MmsGooseControlBlock_getDomain(MmsGooseControlBlock self) { @@ -129,6 +153,12 @@ MmsGooseControlBlock_getDataSet(MmsGooseControlBlock self) return self->dataSet; } +LogicalNode* +MmsGooseControlBlock_getLogicalNode(MmsGooseControlBlock self) +{ + return self->logicalNode; +} + char* MmsGooseControlBlock_getLogicalNodeName(MmsGooseControlBlock self) { @@ -222,7 +252,10 @@ MmsGooseControlBlock_enable(MmsGooseControlBlock self) memcpy(commParameters.dstAddress, MmsValue_getOctetStringBuffer(macAddress), 6); - self->publisher = GoosePublisher_create(&commParameters, self->mmsMapping->gooseInterfaceId); + if (self->gooseInterfaceId) + self->publisher = GoosePublisher_createEx(&commParameters, self->gooseInterfaceId, self->useVlanTag); + else + self->publisher = GoosePublisher_createEx(&commParameters, self->mmsMapping->gooseInterfaceId, self->useVlanTag); self->minTime = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 6)); self->maxTime = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 7)); diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index fdacb6ac..99644625 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -2914,6 +2914,44 @@ MmsMapping_enableGoosePublishing(MmsMapping* self) } +void +MmsMapping_useGooseVlanTag(MmsMapping* self, LogicalNode* ln, const char* gcbName, bool useVlanTag) +{ + LinkedList element = self->gseControls; + + while ((element = LinkedList_getNext(element)) != NULL) { + MmsGooseControlBlock gcb = (MmsGooseControlBlock) element->data; + + if (ln == NULL) { + MmsGooseControlBlock_useGooseVlanTag(gcb, useVlanTag); + } + else { + if ((MmsGooseControlBlock_getLogicalNode(gcb) == ln) && !strcmp(MmsGooseControlBlock_getName(gcb), gcbName)) { + MmsGooseControlBlock_useGooseVlanTag(gcb, useVlanTag); + } + } + } +} + +void +MmsMapping_setGooseInterfaceId(MmsMapping* self, LogicalNode* ln, const char* gcbName, const char* interfaceId) +{ + LinkedList element = self->gseControls; + + while ((element = LinkedList_getNext(element)) != NULL) { + MmsGooseControlBlock gcb = (MmsGooseControlBlock) element->data; + + if (ln == NULL) { + MmsGooseControlBlock_setGooseInterfaceId(gcb, interfaceId); + } + else { + if ((MmsGooseControlBlock_getLogicalNode(gcb) == ln) && !strcmp(MmsGooseControlBlock_getName(gcb), gcbName)) { + MmsGooseControlBlock_setGooseInterfaceId(gcb, interfaceId); + } + } + } +} + void MmsMapping_disableGoosePublishing(MmsMapping* self) { From 241ab5a33d54155f9db7f729965dbba3f63f6b1b Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Tue, 6 Aug 2019 07:44:09 +0200 Subject: [PATCH 4/6] linux socket: TCP_USER_TIMEOUT only defined for kernel version 2.6.37+ --- hal/socket/linux/socket_linux.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hal/socket/linux/socket_linux.c b/hal/socket/linux/socket_linux.c index a89a018d..50f00c64 100644 --- a/hal/socket/linux/socket_linux.c +++ b/hal/socket/linux/socket_linux.c @@ -33,6 +33,7 @@ #include #include #include /* required for TCP keepalive */ +#include #include "hal_thread.h" #include "lib_memory.h" @@ -214,6 +215,7 @@ TcpServerSocket_create(const char* address, int port) int optionReuseAddr = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &optionReuseAddr, sizeof(int)); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) int tcpUserTimeout = 10000; int result = setsockopt(fd, SOL_TCP, TCP_USER_TIMEOUT, &tcpUserTimeout, sizeof(tcpUserTimeout)); @@ -221,6 +223,9 @@ TcpServerSocket_create(const char* address, int port) if (DEBUG_SOCKET) printf("SOCKET: failed to set TCP_USER_TIMEOUT\n"); } +#else +#warning "TCP_USER_TIMEOUT not supported by linux kernel" +#endif if (bind(fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) >= 0) { serverSocket = (ServerSocket) GLOBAL_MALLOC(sizeof(struct sServerSocket)); @@ -312,6 +317,7 @@ TcpSocket_create() self->fd = sock; self->connectTimeout = 5000; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) int tcpUserTimeout = 10000; int result = setsockopt(sock, SOL_TCP, TCP_USER_TIMEOUT, &tcpUserTimeout, sizeof(tcpUserTimeout)); @@ -319,6 +325,8 @@ TcpSocket_create() if (DEBUG_SOCKET) printf("SOCKET: failed to set TCP_USER_TIMEOUT\n"); } +#endif + } else { if (DEBUG_SOCKET) From dfaae6662d20386e6109dec990ddf58c3430841b Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Tue, 6 Aug 2019 07:53:34 +0200 Subject: [PATCH 5/6] - add .NET support for new PSelector type - pSelector is now a byte array in the .NET API --- .../IsoConnectionParameters.cs | 42 +++++++++++++++---- dotnet/example3/Main.cs | 2 +- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/dotnet/IEC61850forCSharp/IsoConnectionParameters.cs b/dotnet/IEC61850forCSharp/IsoConnectionParameters.cs index 759e7f53..5a1cfbe1 100644 --- a/dotnet/IEC61850forCSharp/IsoConnectionParameters.cs +++ b/dotnet/IEC61850forCSharp/IsoConnectionParameters.cs @@ -59,20 +59,28 @@ namespace IEC61850 [MarshalAs(UnmanagedType.ByValArray, SizeConst=16)] public byte[] value; } - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [StructLayout(LayoutKind.Sequential)] + private struct NativePSelector + { + public byte size; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] value; + } + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] private static extern void IsoConnectionParameters_destroy(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] private static extern void IsoConnectionParameters_setRemoteApTitle(IntPtr self, string apTitle, int aeQualifier); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - private static extern void IsoConnectionParameters_setRemoteAddresses(IntPtr self, UInt32 pSelector, NativeSSelector sSelector, NativeTSelector tSelector); + private static extern void IsoConnectionParameters_setRemoteAddresses(IntPtr self, NativePSelector pSelector, NativeSSelector sSelector, NativeTSelector tSelector); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] private static extern void IsoConnectionParameters_setLocalApTitle(IntPtr self, string apTitle, int aeQualifier); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - private static extern void IsoConnectionParameters_setLocalAddresses(IntPtr self, UInt32 pSelector, NativeSSelector sSelector, NativeTSelector tSelector); + private static extern void IsoConnectionParameters_setLocalAddresses(IntPtr self, NativePSelector pSelector, NativeSSelector sSelector, NativeTSelector tSelector); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] private static extern void IsoConnectionParameters_setAcseAuthenticationParameter(IntPtr self, IntPtr acseAuthParameter); @@ -130,7 +138,7 @@ namespace IEC61850 /// /// ISO COTP transport layer address /// - public void SetRemoteAddresses (UInt32 pSelector, byte[] sSelector, byte[] tSelector) + public void SetRemoteAddresses (byte[] pSelector, byte[] sSelector, byte[] tSelector) { if (tSelector.Length > 4) throw new ArgumentOutOfRangeException("tSelector", "maximum size (4) exceeded"); @@ -152,7 +160,17 @@ namespace IEC61850 for (int i = 0; i < sSelector.Length; i++) nativeSSelector.value [i] = sSelector [i]; - IsoConnectionParameters_setRemoteAddresses(self, pSelector, nativeSSelector, nativeTSelector); + if (pSelector.Length > 16) + throw new ArgumentOutOfRangeException("pSelector", "maximum size (16) exceeded"); + + NativePSelector nativePSelector; + nativePSelector.size = (byte)pSelector.Length; + nativePSelector.value = new byte[16]; + + for (int i = 0; i < pSelector.Length; i++) + nativePSelector.value[i] = pSelector[i]; + + IsoConnectionParameters_setRemoteAddresses(self, nativePSelector, nativeSSelector, nativeTSelector); } /// @@ -181,7 +199,7 @@ namespace IEC61850 /// /// ISO COTP transport layer address /// - public void SetLocalAddresses (UInt32 pSelector, byte[] sSelector, byte[] tSelector) + public void SetLocalAddresses (byte[] pSelector, byte[] sSelector, byte[] tSelector) { if (tSelector.Length > 4) throw new ArgumentOutOfRangeException("tSelector", "maximum size (4) exceeded"); @@ -203,7 +221,17 @@ namespace IEC61850 for (int i = 0; i < sSelector.Length; i++) nativeSSelector.value [i] = sSelector [i]; - IsoConnectionParameters_setLocalAddresses(self, pSelector, nativeSSelector, nativeTSelector); + if (pSelector.Length > 16) + throw new ArgumentOutOfRangeException("pSelector", "maximum size (16) exceeded"); + + NativePSelector nativePSelector; + nativePSelector.size = (byte)pSelector.Length; + nativePSelector.value = new byte[16]; + + for (int i = 0; i < pSelector.Length; i++) + nativePSelector.value[i] = pSelector[i]; + + IsoConnectionParameters_setLocalAddresses(self, nativePSelector, nativeSSelector, nativeTSelector); } /// diff --git a/dotnet/example3/Main.cs b/dotnet/example3/Main.cs index f742a31a..224ba145 100644 --- a/dotnet/example3/Main.cs +++ b/dotnet/example3/Main.cs @@ -24,7 +24,7 @@ namespace example3 { IsoConnectionParameters parameters = con.GetConnectionParameters(); - parameters.SetRemoteAddresses(1, new byte[] {0x00, 0x01}, new byte[] {0x00, 0x01, 0x02, 0x03}); + parameters.SetRemoteAddresses(new byte[] { 0x00, 0x01 }, new byte[] {0x00, 0x01}, new byte[] {0x00, 0x01, 0x02, 0x03}); con.ConnectTimeout = 10000; From 4254af3ff900f0f6a9fcff4ed85ed764459b6553 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Wed, 7 Aug 2019 17:49:41 +0200 Subject: [PATCH 6/6] IEC 61850 client: ControlObjectClient_getLastError provides access to data access error information of last control action --- src/iec61850/client/client_control.c | 34 +++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/iec61850/client/client_control.c b/src/iec61850/client/client_control.c index ebe24319..1f64efed 100644 --- a/src/iec61850/client/client_control.c +++ b/src/iec61850/client/client_control.c @@ -57,6 +57,7 @@ struct sControlObjectClient LastApplError lastApplError; MmsError lastMmsError; + MmsDataAccessError lastAccessError; /* last error of read or write command */ CommandTerminationHandler commandTerminationHandler; void* commandTerminaionHandlerParameter; @@ -316,7 +317,10 @@ ControlObjectClient_getCtlValType(ControlObjectClient self) IedClientError ControlObjectClient_getLastError(ControlObjectClient self) { - return iedConnection_mapMmsErrorToIedError(self->lastMmsError); + if (self->lastAccessError != DATA_ACCESS_ERROR_SUCCESS) + return iedConnection_mapDataAccessErrorToIedError(self->lastAccessError); + else + return iedConnection_mapMmsErrorToIedError(self->lastMmsError); } void @@ -501,13 +505,14 @@ ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t MmsError mmsError; - MmsConnection_writeVariable(IedConnection_getMmsConnection(self->connection), + MmsDataAccessError writeResult = MmsConnection_writeVariable(IedConnection_getMmsConnection(self->connection), &mmsError, domainId, itemId, operParameters); MmsValue_setElement(operParameters, 0, NULL); MmsValue_delete(operParameters); self->lastMmsError = mmsError; + self->lastAccessError = writeResult; if (mmsError != MMS_ERROR_NONE) { if (DEBUG_IED_CLIENT) @@ -544,6 +549,9 @@ internalOperateHandler(uint32_t invokeId, void* parameter, MmsError err, MmsData bool success = false; + self->lastMmsError = err; + self->lastAccessError = accessError; + if (iedError == IED_ERROR_OK) { iedError = iedConnection_mapDataAccessErrorToIedError(accessError); @@ -728,6 +736,7 @@ ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal) MmsValue_delete(selValParameters); self->lastMmsError = mmsError; + self->lastAccessError = writeResult; if (mmsError != MMS_ERROR_NONE) { if (DEBUG_IED_CLIENT) @@ -765,6 +774,9 @@ internalSelWithValHandler(uint32_t invokeId, void* parameter, MmsError err, MmsD bool success = false; + self->lastMmsError = err; + self->lastAccessError = accessError; + if (iedError == IED_ERROR_OK) { iedError = iedConnection_mapDataAccessErrorToIedError(accessError); @@ -881,6 +893,7 @@ ControlObjectClient_select(ControlObjectClient self) self->ctlNum++; self->lastMmsError = mmsError; + self->lastAccessError = DATA_ACCESS_ERROR_SUCCESS; if (value == NULL) { if (DEBUG_IED_CLIENT) @@ -899,6 +912,12 @@ ControlObjectClient_select(ControlObjectClient self) selected = true; } } + else if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) { + self->lastAccessError = MmsValue_getDataAccessError(value); + + if (DEBUG_IED_CLIENT) + printf("IED_CLIENT: select returned data-access-error: %i\n", self->lastAccessError); + } else { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: select: unexpected response from server!\n"); @@ -925,12 +944,17 @@ internalSelectHandler(uint32_t invokeId, void* parameter, MmsError err, MmsValue bool success = false; + self->lastMmsError = err; + self->lastAccessError = DATA_ACCESS_ERROR_SUCCESS; + self->ctlNum++; if (iedError == IED_ERROR_OK) { if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) { - iedError = iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value)); + MmsDataAccessError dataAccessError = MmsValue_getDataAccessError(value); + self->lastAccessError = dataAccessError; + iedError = iedConnection_mapDataAccessErrorToIedError(dataAccessError); } else if (MmsValue_getType(value) == MMS_VISIBLE_STRING) { @@ -1082,6 +1106,7 @@ ControlObjectClient_cancel(ControlObjectClient self) &mmsError, domainId, itemId, cancelParameters); self->lastMmsError = mmsError; + self->lastAccessError = writeResult; MmsValue_setElement(cancelParameters, 0, NULL); MmsValue_delete(cancelParameters); @@ -1117,6 +1142,9 @@ internalCancelHandler(uint32_t invokeId, void* parameter, MmsError err, MmsDataA bool success = false; + self->lastMmsError = err; + self->lastAccessError = accessError; + if (iedError == IED_ERROR_OK) { iedError = iedConnection_mapDataAccessErrorToIedError(accessError);