From 982b1097fcb6999f8e52f6f043fb359830aba475 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Tue, 22 Jun 2021 17:17:07 +0200 Subject: [PATCH 1/7] - .NET API: fixed bug - server write access handler causes "CallbackOnCollectedDelegate" exception (LIB61850-236) --- dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs index 79bcd0a5..4cdfbf11 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs @@ -2015,14 +2015,16 @@ namespace IEC61850 private struct WriteAccessHandlerInfo { public WriteAccessHandler handler; + public InternalWriteAccessHandler internalHandler; public object parameter; public DataAttribute dataAttribute; - public WriteAccessHandlerInfo (WriteAccessHandler h, object p, DataAttribute da) + public WriteAccessHandlerInfo (WriteAccessHandler h, object p, DataAttribute da, InternalWriteAccessHandler internalHandler) { handler = h; parameter = p; dataAttribute = da; + this.internalHandler = internalHandler; } } @@ -2309,20 +2311,22 @@ namespace IEC61850 /// a user provided parameter that is passed to the WriteAccessHandler when called. public void HandleWriteAccess (DataAttribute dataAttr, WriteAccessHandler handler, object parameter) { - writeAccessHandlers.Add (dataAttr.self, new WriteAccessHandlerInfo(handler, parameter, dataAttr)); + InternalWriteAccessHandler internalHandler = new InternalWriteAccessHandler(WriteAccessHandlerImpl); + + writeAccessHandlers.Add (dataAttr.self, new WriteAccessHandlerInfo(handler, parameter, dataAttr, internalHandler)); - IedServer_handleWriteAccess (self, dataAttr.self, WriteAccessHandlerImpl, IntPtr.Zero); + IedServer_handleWriteAccess (self, dataAttr.self, internalHandler, IntPtr.Zero); } - private void AddHandlerInfoForDataAttributeRecursive(DataAttribute da, WriteAccessHandler handler, object parameter) + private void AddHandlerInfoForDataAttributeRecursive(DataAttribute da, WriteAccessHandler handler, object parameter, InternalWriteAccessHandler internalHandler) { - writeAccessHandlers.Add(da.self, new WriteAccessHandlerInfo(handler, parameter, da)); + writeAccessHandlers.Add(da.self, new WriteAccessHandlerInfo(handler, parameter, da, internalHandler)); foreach (ModelNode child in da.GetChildren()) { if (child is DataAttribute) { - AddHandlerInfoForDataAttributeRecursive(child as DataAttribute, handler, parameter); + AddHandlerInfoForDataAttributeRecursive(child as DataAttribute, handler, parameter, internalHandler); } } } @@ -2345,11 +2349,18 @@ namespace IEC61850 /// a user provided parameter that is passed to the WriteAccessHandler when called. public void HandleWriteAccessForComplexAttribute(DataAttribute dataAttr, WriteAccessHandler handler, object parameter) { - AddHandlerInfoForDataAttributeRecursive(dataAttr, handler, parameter); + InternalWriteAccessHandler internalHandler = new InternalWriteAccessHandler(WriteAccessHandlerImpl); - IedServer_handleWriteAccessForComplexAttribute(self, dataAttr.self, WriteAccessHandlerImpl, IntPtr.Zero); + AddHandlerInfoForDataAttributeRecursive(dataAttr, handler, parameter, internalHandler); + + IedServer_handleWriteAccessForComplexAttribute(self, dataAttr.self, internalHandler, IntPtr.Zero); } + /// + /// Set the defualt write access policy for a specific FC. The default policy is applied when no handler is installed for a data attribute + /// + /// The functional constraint (FC) + /// The new default access policy public void SetWriteAccessPolicy(FunctionalConstraint fc, AccessPolicy policy) { IedServer_setWriteAccessPolicy (self, fc, policy); From 4ffed8de119117e598c9c2121d9c0c331106c4a6 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 25 Jun 2021 12:55:50 +0200 Subject: [PATCH 2/7] - IED server: fixed crash when IEDName+LDInst is too long --- src/iec61850/server/impl/ied_server.c | 68 +++++++++++-------- src/iec61850/server/mms_mapping/mms_mapping.c | 44 ++++++++---- 2 files changed, 70 insertions(+), 42 deletions(-) diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index c55c115c..b1dcdd17 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -250,6 +250,7 @@ installDefaultValuesForDataAttribute(IedServer self, DataAttribute* dataAttribut char domainName[65]; strncpy(domainName, self->model->name, 64); + domainName[64] = 0; MmsMapping_getMmsDomainFromObjectReference(objectReference, domainName + strlen(domainName)); @@ -451,7 +452,6 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio IedServer self = (IedServer) GLOBAL_CALLOC(1, sizeof(struct sIedServer)); if (self) { - self->model = dataModel; self->running = false; @@ -510,56 +510,63 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio self->mmsMapping = MmsMapping_create(dataModel, self); - self->mmsDevice = MmsMapping_getMmsDeviceModel(self->mmsMapping); + if (self->mmsMapping) { + + self->mmsDevice = MmsMapping_getMmsDeviceModel(self->mmsMapping); - self->mmsServer = MmsServer_create(self->mmsDevice, tlsConfiguration); + self->mmsServer = MmsServer_create(self->mmsDevice, tlsConfiguration); #if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) - if (serverConfiguration) { - MmsServer_enableFileService(self->mmsServer, serverConfiguration->enableFileService); - MmsServer_enableDynamicNamedVariableListService(self->mmsServer, serverConfiguration->enableDynamicDataSetService); - MmsServer_setMaxAssociationSpecificDataSets(self->mmsServer, serverConfiguration->maxAssociationSpecificDataSets); - MmsServer_setMaxDomainSpecificDataSets(self->mmsServer, serverConfiguration->maxDomainSpecificDataSets); - MmsServer_setMaxDataSetEntries(self->mmsServer, serverConfiguration->maxDataSetEntries); - MmsServer_enableJournalService(self->mmsServer, serverConfiguration->enableLogService); - MmsServer_setFilestoreBasepath(self->mmsServer, serverConfiguration->fileServiceBasepath); - MmsServer_setMaxConnections(self->mmsServer, serverConfiguration->maxMmsConnections); - } + if (serverConfiguration) { + MmsServer_enableFileService(self->mmsServer, serverConfiguration->enableFileService); + MmsServer_enableDynamicNamedVariableListService(self->mmsServer, serverConfiguration->enableDynamicDataSetService); + MmsServer_setMaxAssociationSpecificDataSets(self->mmsServer, serverConfiguration->maxAssociationSpecificDataSets); + MmsServer_setMaxDomainSpecificDataSets(self->mmsServer, serverConfiguration->maxDomainSpecificDataSets); + MmsServer_setMaxDataSetEntries(self->mmsServer, serverConfiguration->maxDataSetEntries); + MmsServer_enableJournalService(self->mmsServer, serverConfiguration->enableLogService); + MmsServer_setFilestoreBasepath(self->mmsServer, serverConfiguration->fileServiceBasepath); + MmsServer_setMaxConnections(self->mmsServer, serverConfiguration->maxMmsConnections); + } #endif - MmsMapping_setMmsServer(self->mmsMapping, self->mmsServer); + MmsMapping_setMmsServer(self->mmsMapping, self->mmsServer); - MmsMapping_installHandlers(self->mmsMapping); + MmsMapping_installHandlers(self->mmsMapping); - createMmsServerCache(self); + createMmsServerCache(self); - dataModel->initializer(); + dataModel->initializer(); - installDefaultValuesInCache(self); /* This will also connect cached MmsValues to DataAttributes */ + installDefaultValuesInCache(self); /* This will also connect cached MmsValues to DataAttributes */ - updateDataSetsWithCachedValues(self); + updateDataSetsWithCachedValues(self); - self->clientConnections = LinkedList_create(); + self->clientConnections = LinkedList_create(); - /* default write access policy allows access to SP, SE and SV FCDAs but denies access to DC and CF FCDAs */ - self->writeAccessPolicies = ALLOW_WRITE_ACCESS_SP | ALLOW_WRITE_ACCESS_SV | ALLOW_WRITE_ACCESS_SE; + /* default write access policy allows access to SP, SE and SV FCDAs but denies access to DC and CF FCDAs */ + self->writeAccessPolicies = ALLOW_WRITE_ACCESS_SP | ALLOW_WRITE_ACCESS_SV | ALLOW_WRITE_ACCESS_SE; - MmsMapping_initializeControlObjects(self->mmsMapping); + MmsMapping_initializeControlObjects(self->mmsMapping); #if (CONFIG_IEC61850_REPORT_SERVICE == 1) - Reporting_activateBufferedReports(self->mmsMapping); + Reporting_activateBufferedReports(self->mmsMapping); #endif #if (CONFIG_IEC61850_SETTING_GROUPS == 1) - MmsMapping_configureSettingGroups(self->mmsMapping); + MmsMapping_configureSettingGroups(self->mmsMapping); #endif #if (CONFIG_INCLUDE_GOOSE_SUPPORT) - if (serverConfiguration) { - MmsMapping_useIntegratedGoosePublisher(self->mmsMapping, serverConfiguration->useIntegratedGoosePublisher); - } - + if (serverConfiguration) { + MmsMapping_useIntegratedGoosePublisher(self->mmsMapping, serverConfiguration->useIntegratedGoosePublisher); + } #endif + + } + else { + IedServer_destroy(self); + self = NULL; + } } return self; @@ -602,7 +609,8 @@ IedServer_destroy(IedServer self) if (self->localIpAddress != NULL) GLOBAL_FREEMEM(self->localIpAddress); - MmsMapping_destroy(self->mmsMapping); + if (self->mmsMapping) + MmsMapping_destroy(self->mmsMapping); LinkedList_destroyDeep(self->clientConnections, (LinkedListValueDeleteFunction) private_ClientConnection_destroy); diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index b89898fc..384b8616 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -1712,8 +1712,6 @@ createNamedVariableFromLogicalNode(MmsMapping* self, MmsDomain* domain, } #endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */ - - if (LogicalNode_hasFCData(logicalNode, IEC61850_FC_SV)) { namedVariable->typeSpec.structure.elements[currentComponent] = createFCNamedVariable(logicalNode, IEC61850_FC_SV); @@ -1765,7 +1763,6 @@ createNamedVariableFromLogicalNode(MmsMapping* self, MmsDomain* domain, #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ - currentComponent++; } @@ -1793,12 +1790,19 @@ createMmsDomainFromIedDevice(MmsMapping* self, LogicalDevice* logicalDevice) char domainName[65]; int modelNameLength = strlen(self->model->name); + int ldInstName = strlen(logicalDevice->name); + + if ((modelNameLength + ldInstName) > 64) { + + if (DEBUG_IED_SERVER) + printf("IED_SERVER: Resulting domain name (IEDName+LDInst) too long (%i)\n", modelNameLength + ldInstName); - if (modelNameLength > 64) goto exit_function; + } strncpy(domainName, self->model->name, 64); strncat(domainName, logicalDevice->name, 64 - modelNameLength); + domainName[64] = 0; domain = MmsDomain_create(domainName); @@ -1868,22 +1872,29 @@ exit_function: return domain; } -static void +static bool createMmsDataModel(MmsMapping* self, int iedDeviceCount, MmsDevice* mmsDevice, IedModel* iedModel) { - mmsDevice->domains = (MmsDomain**) GLOBAL_MALLOC((iedDeviceCount) * sizeof(MmsDomain*)); + mmsDevice->domains = (MmsDomain**) GLOBAL_CALLOC(1, (iedDeviceCount) * sizeof(MmsDomain*)); mmsDevice->domainCount = iedDeviceCount; LogicalDevice* logicalDevice = iedModel->firstChild; int i = 0; while (logicalDevice != NULL) { - mmsDevice->domains[i] = createMmsDomainFromIedDevice(self, - logicalDevice); + mmsDevice->domains[i] = createMmsDomainFromIedDevice(self, logicalDevice); + + if (mmsDevice->domains[i] == NULL) { + mmsDevice->domainCount = i; + return false; + } + i++; logicalDevice = (LogicalDevice*) logicalDevice->sibling; } + + return true; } static void @@ -1962,9 +1973,13 @@ createMmsModelFromIedModel(MmsMapping* self, IedModel* iedModel) int iedDeviceCount = IedModel_getLogicalDeviceCount(iedModel); - createMmsDataModel(self, iedDeviceCount, mmsDevice, iedModel); - - createDataSets(mmsDevice, iedModel); + if (createMmsDataModel(self, iedDeviceCount, mmsDevice, iedModel)) { + createDataSets(mmsDevice, iedModel); + } + else { + MmsDevice_destroy(mmsDevice); + mmsDevice = NULL; + } } return mmsDevice; @@ -2015,6 +2030,11 @@ MmsMapping_create(IedModel* model, IedServer iedServer) /* create data model specification */ self->mmsDevice = createMmsModelFromIedModel(self, model); + if (self->mmsDevice == false) { + MmsMapping_destroy(self); + self = NULL; + } + return self; } @@ -2029,7 +2049,7 @@ MmsMapping_destroy(MmsMapping* self) } #endif - if (self->mmsDevice != NULL) + if (self->mmsDevice) MmsDevice_destroy(self->mmsDevice); #if (CONFIG_IEC61850_REPORT_SERVICE == 1) From ca58c703421a9dec014522d85ea763764e8fa21d Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 25 Jun 2021 13:02:05 +0200 Subject: [PATCH 3/7] - MMS server: fixed data race bug in transmitBuffer handling (#338) --- src/mms/iso_mms/server/mms_server.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mms/iso_mms/server/mms_server.c b/src/mms/iso_mms/server/mms_server.c index cfed8824..c387cb8b 100644 --- a/src/mms/iso_mms/server/mms_server.c +++ b/src/mms/iso_mms/server/mms_server.c @@ -279,10 +279,11 @@ MmsServer_reserveTransmitBuffer(MmsServer self) void MmsServer_releaseTransmitBuffer(MmsServer self) { + self->transmitBuffer->size = 0; + #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(self->transmitBufferMutex); #endif - self->transmitBuffer->size = 0; } #if (MMS_OBTAIN_FILE_SERVICE == 1) From 2ace50b712bdfc00cd8145772897fa8b55bd2766 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Mon, 19 Jul 2021 18:44:15 +0200 Subject: [PATCH 4/7] - .NET API: Fixed problem with AccessViolationException in GooseControlBlock.GetDstAddress --- dotnet/IEC61850forCSharp/GooseControlBlock.cs | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/dotnet/IEC61850forCSharp/GooseControlBlock.cs b/dotnet/IEC61850forCSharp/GooseControlBlock.cs index e0bfbe1c..1e91d6fe 100644 --- a/dotnet/IEC61850forCSharp/GooseControlBlock.cs +++ b/dotnet/IEC61850forCSharp/GooseControlBlock.cs @@ -31,7 +31,7 @@ namespace IEC61850 namespace Client { - public class GooseControlBlock { + public class GooseControlBlock : IDisposable { [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr ClientGooseControlBlock_create (string dataAttributeReference); @@ -84,6 +84,18 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern PhyComAddress ClientGooseControlBlock_getDstAddress (IntPtr self); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr ClientGooseControlBlock_getDstAddress_addr(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern byte ClientGooseControlBlock_getDstAddress_priority(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern UInt16 ClientGooseControlBlock_getDstAddress_vid(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern UInt16 ClientGooseControlBlock_getDstAddress_appid(IntPtr self); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void ClientGooseControlBlock_setDstAddress (IntPtr self, PhyComAddress value); @@ -232,7 +244,23 @@ namespace IEC61850 public PhyComAddress GetDstAddress() { - return ClientGooseControlBlock_getDstAddress (self); + PhyComAddress addr = new PhyComAddress(); + + IntPtr value = ClientGooseControlBlock_getDstAddress_addr(self); + + MmsValue mmsValue = new MmsValue(value); + + byte[] dstMacAddr = mmsValue.getOctetString(); + + dstMacAddr.CopyTo(addr.dstAddress, 0); + + addr.dstAddress = dstMacAddr; + + addr.appId = ClientGooseControlBlock_getDstAddress_appid(self); + addr.vlanId = ClientGooseControlBlock_getDstAddress_vid(self); + addr.vlanPriority = ClientGooseControlBlock_getDstAddress_priority(self); + + return addr; } public void SetDstAddress(PhyComAddress value) From bb64d9d8fef7b0ff687a26bc9ff332318b19e58a Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Mon, 19 Jul 2021 18:59:40 +0200 Subject: [PATCH 5/7] - .NET API: GooseSubscriber - added GetGoId, GetGoCbRef, GetFataSet methods - .NET API: GooseReceiver - store references to all added GooseSubscribers to prevent garbage collection - update documentation comments for GooseSubscriber API --- dotnet/IEC61850forCSharp/GooseSubscriber.cs | 62 +++++++++++++++++-- dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs | 2 +- dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs | 6 +- dotnet/goose_subscriber/Program.cs | 22 +++++-- src/goose/goose_subscriber.h | 47 +++++++++++++- 5 files changed, 123 insertions(+), 16 deletions(-) diff --git a/dotnet/IEC61850forCSharp/GooseSubscriber.cs b/dotnet/IEC61850forCSharp/GooseSubscriber.cs index 9b2c6e69..29b64c01 100644 --- a/dotnet/IEC61850forCSharp/GooseSubscriber.cs +++ b/dotnet/IEC61850forCSharp/GooseSubscriber.cs @@ -22,6 +22,7 @@ */ using System; +using System.Collections.Generic; using System.Runtime.InteropServices; using IEC61850.Common; @@ -69,6 +70,8 @@ namespace IEC61850 private bool isDisposed = false; + private List subscribers = new List(); + public GooseReceiver() { self = GooseReceiver_create (); @@ -79,14 +82,29 @@ namespace IEC61850 GooseReceiver_setInterfaceId (self, interfaceId); } + /// + /// Add the subscriber to be handled by this receiver instance + /// + /// A GooseSubscriber can only be added to one GooseReceiver! + /// public void AddSubscriber(GooseSubscriber subscriber) { - GooseReceiver_addSubscriber (self, subscriber.self); + if (subscriber.attachedToReceiver == false) + { + subscriber.attachedToReceiver = true; + GooseReceiver_addSubscriber(self, subscriber.self); + subscribers.Add(subscriber); + } } public void RemoveSubscriber(GooseSubscriber subscriber) { - GooseReceiver_removeSubscriber (self, subscriber.self); + if (subscriber.attachedToReceiver) + { + GooseReceiver_removeSubscriber(self, subscriber.self); + subscribers.Remove(subscriber); + subscriber.attachedToReceiver = false; + } } public void Start() @@ -175,10 +193,22 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] private static extern void GooseSubscriber_setListener (IntPtr self, InternalGooseListener listener, IntPtr parameter); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr GooseSubscriber_getGoId(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr GooseSubscriber_getGoCbRef(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr GooseSubscriber_getDataSet(IntPtr self); + internal IntPtr self; private bool isDisposed = false; + // don't call native destructor when attached to a receiver + internal bool attachedToReceiver = false; + private GooseListener listener = null; private object listenerParameter = null; @@ -214,7 +244,6 @@ namespace IEC61850 return GooseSubscriber_isValid (self); } - public void SetListener(GooseListener listener, object parameter) { this.listener = listener; @@ -227,6 +256,21 @@ namespace IEC61850 } } + public string GetGoId() + { + return Marshal.PtrToStringAnsi(GooseSubscriber_getGoId(self)); + } + + public string GetGoCbRef() + { + return Marshal.PtrToStringAnsi(GooseSubscriber_getGoCbRef(self)); + } + + public string GetDataSet() + { + return Marshal.PtrToStringAnsi(GooseSubscriber_getDataSet(self)); + } + public UInt32 GetStNum() { return GooseSubscriber_getStNum (self); @@ -300,12 +344,20 @@ namespace IEC61850 { if (isDisposed == false) { isDisposed = true; - GooseSubscriber_destroy (self); + + if (attachedToReceiver == false) + GooseSubscriber_destroy (self); + self = IntPtr.Zero; } } - } + ~GooseSubscriber() + { + Dispose(); + } + + } } diff --git a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs index 29315143..58bc9cbd 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs @@ -886,7 +886,7 @@ namespace IEC61850 /// Creates a new SampledValuesControlBlock instance. /// /// The new GoCB instance - /// The object reference of the GoCB + /// The object reference of the GoCB (e.g. "simpleIOGenericIO/LLN0.gcbAnalogValues") public GooseControlBlock GetGooseControlBlock(string gocbObjectReference) { return new GooseControlBlock(gocbObjectReference, connection); diff --git a/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs b/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs index 574d6c82..df4103ed 100644 --- a/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs @@ -86,9 +86,9 @@ namespace IEC61850 public UInt16 vlanId; public UInt16 appId; - [MarshalAs(UnmanagedType.ByValArray, SizeConst=6)] - public byte[] dstAddress = new byte[6]; - } + [MarshalAs(UnmanagedType.ByValArray, SizeConst=6)] + public byte[] dstAddress = new byte[6]; + } /// /// MMS data access error for MmsValue type MMS_DATA_ACCESS_ERROR diff --git a/dotnet/goose_subscriber/Program.cs b/dotnet/goose_subscriber/Program.cs index df84f032..bfcdaff6 100644 --- a/dotnet/goose_subscriber/Program.cs +++ b/dotnet/goose_subscriber/Program.cs @@ -4,6 +4,9 @@ using IEC61850.GOOSE.Subscriber; using System.Threading; using IEC61850.Common; +/// +/// This example is intended to be +/// namespace goose_subscriber { class MainClass @@ -11,9 +14,10 @@ namespace goose_subscriber private static void gooseListener (GooseSubscriber subscriber, object parameter) { Console.WriteLine ("Received GOOSE message:\n-------------------------"); - + Console.WriteLine (" GoID: " + subscriber.GetGoId()); + Console.WriteLine (" GoCbRef: " + subscriber.GetGoCbRef()); + Console.WriteLine (" DatSet: " + subscriber.GetDataSet()); Console.WriteLine (" stNum: " + subscriber.GetStNum ()); - Console.WriteLine (" sqNum: " + subscriber.GetSqNum ()); @@ -33,17 +37,27 @@ namespace goose_subscriber GooseReceiver receiver = new GooseReceiver (); receiver.SetInterfaceId ("eth0"); + //receiver.SetInterfaceId("0"); // on windows use the interface index starting with 0 GooseSubscriber subscriber = new GooseSubscriber ("simpleIOGenericIO/LLN0$GO$gcbAnalogValues"); - subscriber.SetAppId(1000); - + // APP-ID has to match the APP-ID of the publisher + subscriber.SetAppId(4096); subscriber.SetListener (gooseListener, null); receiver.AddSubscriber (subscriber); + GooseSubscriber subscriber2 = new GooseSubscriber("simpleIOGenericIO/LLN0$GO$gcbEvents"); + + subscriber2.SetAppId(4096); + subscriber2.SetListener(gooseListener, null); + + receiver.AddSubscriber(subscriber2); + receiver.Start (); + subscriber = null; + if (receiver.IsRunning ()) { bool running = true; diff --git a/src/goose/goose_subscriber.h b/src/goose/goose_subscriber.h index f88432dd..e58f4f96 100644 --- a/src/goose/goose_subscriber.h +++ b/src/goose/goose_subscriber.h @@ -1,7 +1,7 @@ /* * goose_subscriber.h * - * Copyright 2013-2018 Michael Zillgith + * Copyright 2013-2021 Michael Zillgith * * This file is part of libIEC61850. * @@ -82,12 +82,27 @@ typedef void (*GooseListener)(GooseSubscriber subscriber, void* parameter); LIB61850_API GooseSubscriber GooseSubscriber_create(char* goCbRef, MmsValue* dataSetValues); +/** + * \brief Get the GoId value of the received GOOSE message + * + * \param self GooseSubscriber instance to operate on. + */ LIB61850_API char* GooseSubscriber_getGoId(GooseSubscriber self); +/** + * \brief Get the GOOSE Control Block reference value of the received GOOSE message + * + * \param self GooseSubscriber instance to operate on. + */ LIB61850_API char* GooseSubscriber_getGoCbRef(GooseSubscriber self); +/** + * \brief Get the DatSet value of the received GOOSE message + * + * \param self GooseSubscriber instance to operate on. + */ LIB61850_API char* GooseSubscriber_getDataSet(GooseSubscriber self); @@ -133,6 +148,14 @@ GooseSubscriber_isValid(GooseSubscriber self); LIB61850_API GooseParseError GooseSubscriber_getParseError(GooseSubscriber self); +/** + * \brief Destroy the GooseSubscriber instance + * + * Do not call this function when the GooseSubscriber instance was added to a GooseReceiver. + * The GooseReceiver will call the destructor when \ref GooseReceiver_destroy is called! + * + * \param self GooseSubscriber instance to operate on. + */ LIB61850_API void GooseSubscriber_destroy(GooseSubscriber self); @@ -146,14 +169,32 @@ GooseSubscriber_destroy(GooseSubscriber self); LIB61850_API void GooseSubscriber_setListener(GooseSubscriber self, GooseListener listener, void* parameter); +/** + * \brief Get the APPID value of the received GOOSE message + * + * \param self GooseSubscriber instance to operate on. + */ LIB61850_API int32_t GooseSubscriber_getAppId(GooseSubscriber self); +/** + * \brief Get the source MAC address of the received GOOSE message + * + * \param self GooseSubscriber instance to operate on. + * \param buffer buffer to store the MAC address (at least 6 byte) + */ LIB61850_API void -GooseSubscriber_getSrcMac(GooseSubscriber self, uint8_t *buffer); +GooseSubscriber_getSrcMac(GooseSubscriber self, uint8_t* buffer); +/** + * \brief Get the destination MAC address of the received GOOSE message + * + * \param self GooseSubscriber instance to operate on. + * \param buffer buffer to store the MAC address (at least 6 byte) + */ LIB61850_API void -GooseSubscriber_getDstMac(GooseSubscriber self, uint8_t *buffer); +GooseSubscriber_getDstMac(GooseSubscriber self, uint8_t* buffer); + /** * \brief return the state number (stNum) of the last received GOOSE message. * From c5579758bbd1febb79d7d5339eaf9efb85811b51 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Thu, 29 Jul 2021 12:47:49 +0200 Subject: [PATCH 6/7] - IED server: improved accuracy of integrity report intervals --- src/iec61850/server/mms_mapping/reporting.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c index 25823dc7..525b0d06 100644 --- a/src/iec61850/server/mms_mapping/reporting.c +++ b/src/iec61850/server/mms_mapping/reporting.c @@ -3287,6 +3287,7 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs) if (rc->triggerOps & TRG_OPT_INTEGRITY) { if (rc->intgPd > 0) { + if (currentTimeInMs >= rc->nextIntgReportTime) { /* send current events in event buffer before integrity report */ @@ -3295,12 +3296,24 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs) rc->triggered = false; } - rc->nextIntgReportTime = currentTimeInMs + rc->intgPd; + rc->nextIntgReportTime = rc->nextIntgReportTime + rc->intgPd; + + /* check for system time change effects */ + if ((rc->nextIntgReportTime < currentTimeInMs) || (rc->nextIntgReportTime > currentTimeInMs + rc->intgPd)) { + rc->nextIntgReportTime = currentTimeInMs + rc->intgPd; + } enqueueReport(rc, true, false, currentTimeInMs); rc->triggered = false; } + else { + /* check for system time change effects */ + if ((rc->nextIntgReportTime < currentTimeInMs) || (rc->nextIntgReportTime > currentTimeInMs + rc->intgPd)) { + rc->nextIntgReportTime = currentTimeInMs + rc->intgPd; + } + + } } } From f0c1d6348e17c254b891f0f1e7cd97436329013e Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Tue, 10 Aug 2021 16:32:42 +0200 Subject: [PATCH 7/7] - removed debug output --- hal/socket/linux/socket_linux.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hal/socket/linux/socket_linux.c b/hal/socket/linux/socket_linux.c index 2a7f7901..795b0eb8 100644 --- a/hal/socket/linux/socket_linux.c +++ b/hal/socket/linux/socket_linux.c @@ -660,10 +660,6 @@ Socket_write(Socket self, uint8_t* buf, int size) printf("DEBUG_SOCKET: send returned error (errno=%i)\n", errno); } } - else { - if (size != retVal) - printf("send(%i)->%i\n", size, retVal); - } return retVal; }