/* ======================================================================== * Copyright (c) 2005-2020 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 System.Threading; using Opc.Ua; using Opc.Ua.PubSub; namespace Quickstarts.ConsoleReferencePublisher { class PublishedValuesWrites { #region Fields // It should match the namespace index from configuration file public const ushort NamespaceIndexSimple = 2; public const ushort NamespaceIndexAllTypes = 3; private const string DataSetNameSimple = "Simple"; private const string DataSetNameAllTypes = "AllTypes"; // simulate for BoolToogle changes to 3 seconds private int m_boolToogleCount = 0; private const int BoolToogleLimit = 2; private const int SimpleInt32Limit = 10000; private FieldMetaDataCollection m_simpleFields = new FieldMetaDataCollection(); private FieldMetaDataCollection m_allTypesFields = new FieldMetaDataCollection(); private PublishedDataSetDataTypeCollection m_publishedDataSets; private IUaPubSubDataStore m_dataStore; private Timer m_updateValuesTimer; string[] m_aviationAlphabet = new string[] { "Alfa", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", "November", "Oscar", "Papa", "Quebec", "Romeo", "Sierra", "Tango", "Uniform", "Victor", "Whiskey", "X-Ray", "Yankee", "Zulu" }; int m_aviationAlphabetIndex = 0; private object m_lock = new object(); #endregion #region Constructor /// /// Constructor /// /// public PublishedValuesWrites(UaPubSubApplication uaPubSubApplication) { m_publishedDataSets = uaPubSubApplication.UaPubSubConfigurator.PubSubConfiguration.PublishedDataSets; m_dataStore = uaPubSubApplication.DataStore; } #endregion #region IDisposable public void Dispose() { m_updateValuesTimer.Dispose(); } #endregion #region Public Methods /// /// Initialize PublisherData with information from configuration and start timer to update data /// public void Start() { if (m_publishedDataSets != null) { // Remember the fields to be updated foreach (var publishedDataSet in m_publishedDataSets) { switch (publishedDataSet.Name) { case DataSetNameSimple: m_simpleFields.AddRange(publishedDataSet.DataSetMetaData.Fields); break; case DataSetNameAllTypes: m_allTypesFields.AddRange(publishedDataSet.DataSetMetaData.Fields); break; } } } try { LoadInitialData(); } catch (Exception e) { Utils.Trace(Utils.TraceMasks.Error, "SamplePublisher.DataStoreValuesGenerator.LoadInitialData wrong field: {0}", e.StackTrace); } m_updateValuesTimer = new Timer(UpdateValues, null, 1000, 1000); } #endregion #region Private Methods /// /// Load initial demo data /// private void LoadInitialData() { #region DataSet 'Simple' fill with data WriteFieldData("BoolToggle", NamespaceIndexSimple, new DataValue(new Variant(false), StatusCodes.Good, DateTime.UtcNow)); WriteFieldData("Int32", NamespaceIndexSimple, new DataValue(new Variant(0), StatusCodes.Good, DateTime.UtcNow)); WriteFieldData("Int32Fast", NamespaceIndexSimple, new DataValue(new Variant(0), StatusCodes.Good, DateTime.UtcNow)); WriteFieldData("DateTime", NamespaceIndexSimple, new DataValue(new Variant(DateTime.UtcNow), StatusCodes.Good, DateTime.UtcNow)); #endregion #region DataSet 'AllTypes' fill with data WriteFieldData("BoolToggle", NamespaceIndexAllTypes, new DataValue(new Variant(true), StatusCodes.Good, DateTime.UtcNow)); WriteFieldData("Byte", NamespaceIndexAllTypes, new DataValue(new Variant((byte)0), StatusCodes.Good, DateTime.UtcNow)); WriteFieldData("Int16", NamespaceIndexAllTypes, new DataValue(new Variant((Int16)0), StatusCodes.Good, DateTime.UtcNow)); WriteFieldData("Int32", NamespaceIndexAllTypes, new DataValue(new Variant(0), StatusCodes.Good, DateTime.UtcNow)); WriteFieldData("SByte", NamespaceIndexAllTypes, new DataValue(new Variant((sbyte)0), StatusCodes.Good, DateTime.UtcNow)); WriteFieldData("UInt16", NamespaceIndexAllTypes, new DataValue(new Variant((UInt16)0), StatusCodes.Good, DateTime.UtcNow)); WriteFieldData("UInt32", NamespaceIndexAllTypes, new DataValue(new Variant((UInt32)0), StatusCodes.Good, DateTime.UtcNow)); WriteFieldData("UInt64", NamespaceIndexAllTypes, new DataValue(new Variant((UInt64)0), StatusCodes.Good, DateTime.UtcNow)); WriteFieldData("Float", NamespaceIndexAllTypes, new DataValue(new Variant((float)0F), StatusCodes.Good, DateTime.UtcNow)); WriteFieldData("Double", NamespaceIndexAllTypes, new DataValue(new Variant((double)0.0), StatusCodes.Good, DateTime.UtcNow)); WriteFieldData("String", NamespaceIndexAllTypes, new DataValue(new Variant(m_aviationAlphabet[0]), StatusCodes.Good, DateTime.UtcNow)); WriteFieldData("ByteString", NamespaceIndexAllTypes, new DataValue(new Variant(new byte[] { 1, 2, 3 }), StatusCodes.Good, DateTime.UtcNow)); WriteFieldData("Guid", NamespaceIndexAllTypes, new DataValue(new Variant(Guid.NewGuid()), StatusCodes.Good, DateTime.UtcNow)); WriteFieldData("DateTime", NamespaceIndexAllTypes, new DataValue(new Variant(DateTime.UtcNow), StatusCodes.Good, DateTime.UtcNow)); WriteFieldData("UInt32Array", NamespaceIndexAllTypes, new DataValue(new Variant(new UInt32[] { 1, 2, 3 }), StatusCodes.Good, DateTime.UtcNow)); #endregion } /// /// Write (update) field data /// /// /// private void WriteFieldData(string metaDatafieldName, ushort namespaceIndex, DataValue dataValue) { m_dataStore.WritePublishedDataItem(new NodeId(metaDatafieldName, namespaceIndex), Attributes.Value, dataValue); } /// /// Simulate value changes in dynamic nodes /// /// private void UpdateValues(object state) { try { lock (m_lock) { foreach (FieldMetaData variable in m_simpleFields) { switch (variable.Name) { case "BoolToggle": m_boolToogleCount++; if (m_boolToogleCount >= BoolToogleLimit) { m_boolToogleCount = 0; IncrementValue(variable, NamespaceIndexSimple); } break; case "Int32": IncrementValue(variable, NamespaceIndexSimple, SimpleInt32Limit); break; case "Int32Fast": IncrementValue(variable, NamespaceIndexSimple, SimpleInt32Limit, 100); break; case "DateTime": IncrementValue(variable, NamespaceIndexSimple); break; } } foreach (FieldMetaData variable in m_allTypesFields) { IncrementValue(variable, NamespaceIndexAllTypes); } } } catch (Exception e) { Utils.Trace(e, "Unexpected error doing simulation."); } } /// /// Increment value /// maxAllowedValue - maximum incremented value before reset value to initial value /// step - the increment value /// /// /// /// /// private void IncrementValue(FieldMetaData variable, ushort namespaceIndex, long maxAllowedValue = Int32.MaxValue, int step = 0) { // Read value to be incremented DataValue dataValue = m_dataStore.ReadPublishedDataItem(new NodeId(variable.Name, namespaceIndex), Attributes.Value); if (dataValue.Value == null) { return; } bool valueUpdated = false; BuiltInType expectedType = TypeInfo.GetBuiltInType(variable.DataType); switch (expectedType) { case BuiltInType.Boolean: if (variable.ValueRank == ValueRanks.Scalar) { Boolean boolValue = Convert.ToBoolean(dataValue.Value); dataValue.Value = !boolValue; valueUpdated = true; } break; case BuiltInType.Byte: if (variable.ValueRank == ValueRanks.Scalar) { byte byteValue = Convert.ToByte(dataValue.Value); dataValue.Value = (byte)(byteValue + 1); valueUpdated = true; } break; case BuiltInType.Int16: if (variable.ValueRank == ValueRanks.Scalar) { Int16 int16Value = Convert.ToInt16(dataValue.Value); int intIdentifier = int16Value; Interlocked.CompareExchange(ref intIdentifier, 0, Int16.MaxValue); dataValue.Value = (Int16)Interlocked.Increment(ref intIdentifier); valueUpdated = true; } break; case BuiltInType.Int32: if (variable.ValueRank == ValueRanks.Scalar) { Int32 int32Value = Convert.ToInt32(dataValue.Value); if (step > 0) { int32Value += (step - 1); } if (int32Value > maxAllowedValue) { int32Value = 0; } dataValue.Value = Interlocked.Increment(ref int32Value); valueUpdated = true; } break; case BuiltInType.SByte: if (variable.ValueRank == ValueRanks.Scalar) { SByte sbyteValue = Convert.ToSByte(dataValue.Value); int intIdentifier = sbyteValue; Interlocked.CompareExchange(ref intIdentifier, 0, SByte.MaxValue); dataValue.Value = (SByte)Interlocked.Increment(ref intIdentifier); valueUpdated = true; } break; case BuiltInType.UInt16: if (variable.ValueRank == ValueRanks.Scalar) { UInt16 uint16Value = Convert.ToUInt16(dataValue.Value); int intIdentifier = uint16Value; Interlocked.CompareExchange(ref intIdentifier, 0, UInt16.MaxValue); dataValue.Value = (UInt16)Interlocked.Increment(ref intIdentifier); valueUpdated = true; } break; case BuiltInType.UInt32: if (variable.ValueRank == ValueRanks.Scalar) { UInt32 uint32Value = Convert.ToUInt32(dataValue.Value); long longIdentifier = uint32Value; Interlocked.CompareExchange(ref longIdentifier, 0, UInt32.MaxValue); dataValue.Value = (UInt32)Interlocked.Increment(ref longIdentifier); valueUpdated = true; } else if (variable.ValueRank == ValueRanks.OneDimension) { uint[] values = dataValue.Value as uint[]; if (values != null) { for (int i = 0; i < values.Length; i++) { UInt32 uint32Value = values[i]; long longIdentifier = uint32Value; Interlocked.CompareExchange(ref longIdentifier, 0, UInt32.MaxValue); values[i] = (UInt32)Interlocked.Increment(ref longIdentifier); } valueUpdated = true; } } break; case BuiltInType.UInt64: if (variable.ValueRank == ValueRanks.Scalar) { UInt64 uint64Value = Convert.ToUInt64(dataValue.Value); float longIdentifier = uint64Value + 1; Interlocked.CompareExchange(ref longIdentifier, 0, UInt64.MaxValue); dataValue.Value = (UInt64)longIdentifier; valueUpdated = true; } break; case BuiltInType.Float: if (variable.ValueRank == ValueRanks.Scalar) { float floatValue = Convert.ToSingle(dataValue.Value); Interlocked.CompareExchange(ref floatValue, 0, float.MaxValue); dataValue.Value = floatValue + 1; valueUpdated = true; } break; case BuiltInType.Double: if (variable.ValueRank == ValueRanks.Scalar) { double doubleValue = Convert.ToDouble(dataValue.Value); Interlocked.CompareExchange(ref doubleValue, 0, double.MaxValue); dataValue.Value = doubleValue + 1; valueUpdated = true; } break; case BuiltInType.DateTime: if (variable.ValueRank == ValueRanks.Scalar) { dataValue.Value = DateTime.UtcNow; valueUpdated = true; } break; case BuiltInType.Guid: if (variable.ValueRank == ValueRanks.Scalar) { dataValue.Value = Guid.NewGuid(); valueUpdated = true; } break; case BuiltInType.String: if (variable.ValueRank == ValueRanks.Scalar) { m_aviationAlphabetIndex = (m_aviationAlphabetIndex + 1) % m_aviationAlphabet.Length; dataValue.Value = m_aviationAlphabet[m_aviationAlphabetIndex]; valueUpdated = true; } break; } if (valueUpdated) { // Save new updated value to data store WriteFieldData(variable.Name, namespaceIndex, dataValue); } } #endregion } }