Skip to content

Commit

Permalink
Support Metadata frames in PubSub (#1518)
Browse files Browse the repository at this point in the history
    Refactored WriterGroupPublishState out of IPubSubConnection file
    JSON Discovery messages for DataSetMetaData
    UADP Discovery messages for DataSetMetaData (Request + response)
    Refactored the UaPublisher, extracted IntervalRunner and reused it in UdpDiscoveryPublisher & UdpDiscoverySubscriber
    Proposal: automatic setting of ConfigurationVersion based on the rules in 6.2.2.1.5 (part 14)in new class Opc.Ua.PubSub.Configuration.ConfigurationVersionUtils
    New events added to UaPubSubApplication
    Implemented support for UADP Data Key Frame DataSetMessages

    Unit tests were updated to cover the new functionality
  • Loading branch information
mrsuciu committed Sep 28, 2021
1 parent 33ae8bb commit 75744e2
Show file tree
Hide file tree
Showing 51 changed files with 7,090 additions and 1,856 deletions.
228 changes: 190 additions & 38 deletions Applications/ConsoleReferencePublisher/Program.cs

Large diffs are not rendered by default.

358 changes: 297 additions & 61 deletions Applications/ConsoleReferenceSubscriber/Program.cs

Large diffs are not rendered by default.

156 changes: 156 additions & 0 deletions Libraries/Opc.Ua.PubSub/Configuration/ConfigurationVersionUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/* ========================================================================
* Copyright (c) 2005-2021 The OPC Foundation, Inc. All rights reserved.
*
* OPC Foundation MIT License 1.00
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* The complete license agreement can be found here:
* http://opcfoundation.org/License/MIT/1.00/
* ======================================================================*/


using System;

namespace Opc.Ua.PubSub.Configuration
{
/// <summary>
/// Helper class that calculates the ConfigurationVersion for MetaData
/// </summary>
public class ConfigurationVersionUtils
{
// The epoch date is midnight UTC (00:00) on January 1, 2000.
private static readonly DateTime kEpochDate = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc);

/// <summary>
/// Analyze and decide the right ConfigurationVersion for new MetaData
/// </summary>
/// <param name="oldMetaData">The historical MetaData to be compared against the new MetaData</param>
/// <param name="newMetaData">The new MetaData </param>
/// <returns></returns>
public static ConfigurationVersionDataType CalculateConfigurationVersion(DataSetMetaDataType oldMetaData, DataSetMetaDataType newMetaData)
{
if (newMetaData == null)
{
throw new ArgumentNullException(nameof(newMetaData));
}

bool hasMinorVersionChange = false;
bool hasMajorVersionChange = false;

if (oldMetaData == null)
{
// create first version of ConfigurationVersion
hasMajorVersionChange = true;
}
else
{
/*Removing fields from the DataSet content, reordering fields, adding fields in between other fields or a
* DataType change in fields shall result in an update of the MajorVersion. */
// check if any field was deleted
if (oldMetaData.Fields.Count > newMetaData.Fields.Count)
{
hasMajorVersionChange = true;
}
else
{
// compare fileds
for (int i = 0; i < oldMetaData.Fields.Count; i++)
{
/*If at least one Property value of a DataSetMetaData field changes, the MajorVersion shall be updated.*/
if (!Utils.IsEqual(oldMetaData.Fields[i].Properties, newMetaData.Fields[1].Properties))
{
hasMajorVersionChange = true;
break;
}
}
if (!hasMajorVersionChange && oldMetaData.Fields.Count < newMetaData.Fields.Count)
{
/* Only the MinorVersion shall be updated if fields are added at the end of the DataSet content.*/
hasMinorVersionChange = true;
}
}
}

if (hasMajorVersionChange || hasMinorVersionChange)
{
UInt32 versionTime = CalculateVersionTime(DateTime.UtcNow);
if (hasMajorVersionChange)
{
// Change both minor and major version
return new ConfigurationVersionDataType() {
MinorVersion = versionTime,
MajorVersion = versionTime
};
}
else
{
// only minor version was changed
return new ConfigurationVersionDataType() {
MinorVersion = versionTime,
MajorVersion = newMetaData.ConfigurationVersion.MajorVersion
};
}
}

// there is no change
return new ConfigurationVersionDataType() {
MinorVersion = newMetaData.ConfigurationVersion.MinorVersion,
MajorVersion = newMetaData.ConfigurationVersion.MajorVersion
};

}

/// <summary>
/// Calculate and return the VersionTime calculated for the input parameter
/// </summary>
/// <param name="timeOfConfiguration">The current time of configuration</param>
/// <returns></returns>
public static UInt32 CalculateVersionTime(DateTime timeOfConfiguration)
{
/*This primitive data type is a UInt32 that represents the time in seconds since the year 2000. The epoch date is midnight UTC (00:00) on January 1, 2000.
It is used as version number based on the last change time. If the version is updated, the new value shall be greater than the previous value.
If a Variable is initialized with a VersionTime value, the value must be either loaded from persisted configuration or time synchronization must be available to ensure a unique version is applied.
The value 0 is used to indicate that no version information is available.*/
return (uint)timeOfConfiguration.Subtract(kEpochDate).TotalSeconds;
}

/// <summary>
/// Check if the DataSetMetData is usable for decoding.
/// It shall be not null and have the Fields collection defined and also the ConfigurationVersion shall be not null or Empty
/// </summary>
/// <param name="dataSetMetaData"></param>
/// <returns></returns>
public static bool IsUsable(DataSetMetaDataType dataSetMetaData)
{
if (dataSetMetaData == null) return false;
if (dataSetMetaData.Fields == null) return false;
if (dataSetMetaData.Fields.Count == 0) return false;

if (dataSetMetaData.ConfigurationVersion == null) return false;
if (dataSetMetaData.ConfigurationVersion.MajorVersion == 0) return false;
if (dataSetMetaData.ConfigurationVersion.MinorVersion == 0) return false;

return true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
* http://opcfoundation.org/License/MIT/1.00/
* ======================================================================*/

using Opc.Ua;
using System;

namespace Opc.Ua.PubSub.Configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,10 @@ public static class UaPubSubConfigurationHelper
/// <summary>
/// Save a <see cref="PubSubConfigurationDataType"/> instance as XML
/// </summary>
/// <param name="pubSubConfiguration">The configuratiomn object that shall be saved in the file.</param>
/// <param name="pubSubConfiguration">The configuration object that shall be saved in the file.</param>
/// <param name="filePath">The file path from where the configuration shall be saved.</param>
public static void SaveConfiguration(PubSubConfigurationDataType pubSubConfiguration, string filePath)
{

Stream ostrm = File.Open(filePath, FileMode.Create, FileAccess.ReadWrite);

XmlWriterSettings settings = new XmlWriterSettings();
Expand Down
59 changes: 59 additions & 0 deletions Libraries/Opc.Ua.PubSub/ConfigurationUpdatingEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/* ========================================================================
* Copyright (c) 2005-2021 The OPC Foundation, Inc. All rights reserved.
*
* OPC Foundation MIT License 1.00
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* The complete license agreement can be found here:
* http://opcfoundation.org/License/MIT/1.00/
* ======================================================================*/

using System;

namespace Opc.Ua.PubSub
{
/// <summary>
/// Class that contains data related to ConfigurationUpdating event
/// </summary>
public class ConfigurationUpdatingEventArgs : EventArgs
{
/// <summary>
/// The Property of <see cref="Parent"/> that should receive <see cref="NewValue"/>.
/// </summary>
public ConfigurationProperty ChangedProperty { get; set; }

/// <summary>
/// The the configuration object that should receive a <see cref="NewValue"/> in its <see cref="ChangedProperty"/>.
/// </summary>
public object Parent { get; set; }

/// <summary>
/// The new value that shall be set to the <see cref="Parent"/> in <see cref="ChangedProperty"/> property.
/// </summary>
public object NewValue { get; set; }

/// <summary>
/// Flag that indicates if the Configuration update should be canceled.
/// </summary>
public bool Cancel { get; set; }
}
}
107 changes: 107 additions & 0 deletions Libraries/Opc.Ua.PubSub/DataSetDecodeErrorEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/* ========================================================================
* Copyright (c) 2005-2021 The OPC Foundation, Inc. All rights reserved.
*
* OPC Foundation MIT License 1.00
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* The complete license agreement can be found here:
* http://opcfoundation.org/License/MIT/1.00/
* ======================================================================*/

using System;
using Opc.Ua.PubSub.Encoding;

namespace Opc.Ua.PubSub
{
/// <summary>
/// Class that contains data related to DataSetDecodeErrorOccurred event
/// </summary>
public class DataSetDecodeErrorEventArgs : EventArgs
{
#region Private members
private DataSetDecodeErrorReason m_dataSetDecodeErrorReason;
private UaNetworkMessage m_networkMessage;
private DataSetReaderDataType m_dataSetReader;
#endregion

#region Constructor
/// <summary>
/// Constructor
/// </summary>
/// <param name="dataSetDecodeErrorReason"></param>
/// <param name="networkMessage"></param>
/// <param name="dataSetReader"></param>
public DataSetDecodeErrorEventArgs(DataSetDecodeErrorReason dataSetDecodeErrorReason, UaNetworkMessage networkMessage, DataSetReaderDataType dataSetReader)
{
m_dataSetDecodeErrorReason = dataSetDecodeErrorReason;
m_networkMessage = networkMessage;
m_dataSetReader = dataSetReader;
}
#endregion

#region Public Properties
/// <summary>
/// The reason for triggering the DataSetDecodeErrorOccurred event
/// </summary>
public DataSetDecodeErrorReason DecodeErrorReason
{
get
{
return m_dataSetDecodeErrorReason;
}
set
{
m_dataSetDecodeErrorReason = value;
}
}

/// <summary>
/// The DataSetMessage on which the decoding operated
/// </summary>
public UaNetworkMessage UaNetworkMessage
{
get
{
return m_networkMessage;
}
set
{
m_networkMessage = value;
}
}
/// <summary>
/// The DataSetReader used by the decoding operation
/// </summary>
public DataSetReaderDataType DataSetReader
{
get
{
return m_dataSetReader;
}
set
{
m_dataSetReader = value;
}
}
#endregion
}
}

0 comments on commit 75744e2

Please sign in to comment.