diff --git a/MTC_Adapter/OPC-UA-REDIS/DataItemRed.cs b/MTC_Adapter/OPC-UA-REDIS/DataItemRed.cs
index e290794..c2890fd 100644
--- a/MTC_Adapter/OPC-UA-REDIS/DataItemRed.cs
+++ b/MTC_Adapter/OPC-UA-REDIS/DataItemRed.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Linq;
namespace OPC_UA_REDIS
{
@@ -118,4 +119,381 @@ namespace OPC_UA_REDIS
return list;
}
}
+
+ ///
+ /// A condition handles the fact that a single condition can have multiple
+ /// activations and needs to check when the are present and are cleared.
+ ///
+ public class ConditionRed : DataItemRed
+ {
+ ///
+ /// The four values for the condition.
+ ///
+ public enum Level
+ {
+ UNAVAILABLE,
+ NORMAL,
+ WARNING,
+ FAULT
+ }
+
+ ///
+ /// The Activation is itself a data item. This is so it can be cleared
+ /// and treated like other data items when generating text.
+ ///
+ public class Active : DataItemRed
+ {
+ // The pieces of the activation are only used by the condition.
+ public Level mLevel;
+ public string mText;
+ public string mNativeCode;
+ public string mNativeSeverity;
+ public string mQualifier;
+
+ ///
+ /// The marked flag tells if this alarm has been reasserted.
+ ///
+ public bool mMarked = true;
+
+ ///
+ /// A placeholder activation is just a normal or unavailable.
+ ///
+ public bool mPlaceholder = false;
+
+ ///
+ /// Create a new activation
+ ///
+ /// The name of the condition, passed from the parent
+ /// The condition level
+ /// The descriptive text for the condition
+ /// The native code of the alarm or warning
+ /// A high/low qualifier
+ /// The native severity of the condition
+ public Active(string name, Level level, string text = "", string code = "",
+ string qualifier = "", string severity = "")
+ : base(name)
+ {
+ mLevel = level;
+ mText = text;
+ mNativeCode = code;
+ mQualifier = qualifier;
+ mNativeSeverity = severity;
+
+ if (mNativeCode.Length == 0 && (mLevel == Level.NORMAL || mLevel == Level.UNAVAILABLE))
+ mPlaceholder = true;
+ }
+
+ ///
+ /// Update the values of the activation. Also marks this activation.
+ ///
+ /// The condition level
+ /// The descriptive text for the condition
+ /// A high/low qualifier
+ /// The native severity of the condition
+ /// true if the condition has changed
+ public bool Set(Level level, string text = "",
+ string qualifier = "", string severity = "")
+ {
+ mChanged = level != mLevel || text != mText || qualifier != mQualifier ||
+ severity != mNativeSeverity;
+ if (mChanged)
+ {
+ mLevel = level;
+ mQualifier = qualifier;
+ mText = text;
+ mNativeSeverity = severity;
+ }
+
+ mMarked = true;
+ return mChanged;
+ }
+
+ public override string ToString()
+ {
+ return mName + "|" + Enum.GetName(mLevel.GetType(), mLevel) + "|" + mNativeCode + "|" + mNativeSeverity + "|" + mQualifier + "|" + mText;
+ }
+
+ ///
+ /// Resets the marked flag.
+ ///
+ public void Clear()
+ {
+ mMarked = false;
+ }
+ }
+
+ ///
+ /// A flag to indicate that the mark and sweep has begun.
+ ///
+ bool mBegun = false;
+
+ ///
+ /// A flag indicating the second phase of the mark and sweep has completed.
+ ///
+ bool mPrepared = false;
+
+ ///
+ /// true means this is a simple condition and does not require
+ /// mark and sweep processing.
+ ///
+ bool mSimple;
+ List mActiveList = new List();
+
+ ///
+ /// Create a new condition
+ ///
+ /// The name of the data item
+ /// If this is a simple condition or if it uses
+ /// mark and sweep
+ public ConditionRed(String name, bool simple = false) : base(name)
+ {
+ mSimple = simple;
+ Add(new Active(mName, Level.UNAVAILABLE));
+ }
+
+ ///
+ /// Make this condition unavailable
+ ///
+ public override void Unavailable()
+ {
+ Add(Level.UNAVAILABLE);
+ }
+
+ ///
+ /// This clears all the marks and begins so we can tell which
+ /// conditions were not added during this pass. This is not
+ /// required for simple conditions.
+ ///
+ public override void Begin()
+ {
+ if (!mSimple)
+ {
+ foreach (Active active in mActiveList)
+ active.Clear();
+ mBegun = true;
+ }
+
+ mPrepared = mChanged = false;
+ }
+
+ ///
+ /// This is called before we send the actual changed data. It
+ /// does the diff from the previous state and finds all the
+ /// activations that need to be removed. This also check to see
+ /// if all the activations have been removed because we only
+ /// need to do a single normal with no native code to clear all.
+ ///
+ /// This is not required for simple conditions.
+ ///
+ public override void Prepare()
+ {
+ if (mBegun)
+ {
+ bool marked = false;
+
+ // Check to see if we have any active marked conditions
+ foreach (Active active in mActiveList)
+ {
+ if (active.mPlaceholder || active.mMarked)
+ {
+ marked = true;
+ break;
+ }
+ }
+
+ // If they've all been cleared, then go back to normal.
+ if (!marked) Normal();
+
+ // Sweep the old conditions and if they are not marked
+ // set them to normal.
+ foreach (Active active in mActiveList)
+ {
+ if (!active.mPlaceholder && !active.mMarked)
+ {
+ active.Set(Level.NORMAL, "");
+ active.mMarked = false;
+ }
+ if (active.Changed)
+ mChanged = true;
+ }
+
+ mPrepared = true;
+ }
+ }
+
+ ///
+ /// This is the sweep phase where we removed the changed flags
+ /// and remove all the stale activations.
+ ///
+ public override void Cleanup()
+ {
+ base.Cleanup();
+
+ mBegun = mPrepared = false;
+ foreach (Active active in mActiveList.ToList())
+ {
+ // It is assumed that if the activations are no longer needed, they will
+ // be removed here after they are returned.
+ if (!active.mPlaceholder && !active.mMarked)
+ mActiveList.Remove(active);
+
+ active.Cleanup();
+ }
+
+ // Remvoe stale items from the active list that are not marked
+ }
+
+ ///
+ /// Add a new activation.
+ ///
+ ///
+ private void Add(Active active)
+ {
+ mActiveList.Add(active);
+ }
+
+ ///
+ /// Adds a new activation to the condition or if normal or unavailable, removes the
+ /// activation.
+ ///
+ /// The level
+ /// The descriptive text for the condition
+ /// The native code
+ /// The qualifier
+ /// The native severity
+ /// true if the activation is modified
+ public bool Add(Level level, string text = "", string code = "", string qualifier = "", string severity = "")
+ {
+ bool result = false;
+
+ // Get the first activation
+ Active first = null;
+ if (mActiveList.Count > 0)
+ first = mActiveList.First();
+
+ // Check for a reset of all conditions for a normal or an unavailable
+ if ((level == Level.NORMAL || level == Level.UNAVAILABLE) && code.Length == 0)
+ {
+ // If we haven't changed.
+ if (mActiveList.Count == 1 && first.mLevel == level)
+ first.mMarked = true;
+ else
+ {
+ // Create a new placeholder activation. We don't need to remember the
+ // old activations because the global normal will reset everything.
+ mActiveList.Clear();
+ Add(new Active(mName, level));
+ result = mChanged = true;
+ }
+ }
+ else
+ {
+ // If the first entry is a normal or unavailable and we are entering
+ // into a warning or fault, remove the normal or unavailable
+ if (mActiveList.Count() == 1 && first.mPlaceholder)
+ {
+ mActiveList.Clear();
+ }
+
+ // See if we can find the activation with the same native code.
+ Active found = mActiveList.Find(delegate (Active ak) { return ak.mNativeCode == code; });
+
+ if (found != null)
+ {
+ // If we found it, update all the properties and see if it has changed.
+ // This will mark this activation
+ result = found.Set(level, text, qualifier, severity);
+ mChanged = mChanged || result;
+ }
+ else
+ {
+ // Otherwise, we have a new activation and we should create a new one.
+ Add(new Active(mName, level, text, code, qualifier, severity));
+ result = mChanged = true;
+ }
+ }
+
+ return result;
+ }
+
+ ///
+ /// Clear a condition from the active list - Used only for simple Conditions.
+ ///
+ /// The native code
+ /// true if the activation is found
+ public bool Clear(string code)
+ {
+ // Find the activation.
+ Active found = mActiveList.Find(delegate (Active ak) { return ak.mNativeCode == code; });
+
+ if (found != null)
+ {
+ // If we've removed the last activation, go back to normal.
+ if (mActiveList.Count() == 1)
+ Add(Level.NORMAL);
+ else
+ {
+ // Otherwise, just clear this one.
+ found.Set(Level.NORMAL);
+ // Clear makes the activation be removed next sweep.
+ found.Clear();
+ }
+ mChanged = true;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ // Cover to set everything to normal.
+ public bool Normal()
+ {
+ return Add(Level.NORMAL);
+ }
+
+ ///
+ /// Used to get a list of the active conditions for writing out to
+ /// the clients.
+ ///
+ /// This flag is used to get all activations, regardless
+ /// of their changed state. This is used to deliver initial state to the client
+ /// A list of activations (also DataItems)
+ public override List ItemList(bool all = false)
+ {
+ List list = new List();
+ if (all)
+ {
+ // Just grab all the activations.
+ foreach (Active active in mActiveList)
+ list.Add(active);
+ }
+ else if (mSimple)
+ {
+ // For a simple condition, we are only looking for the changed set.
+ // Since we don't care about the mark and sweep, this is similar to
+ // all other data items.
+ foreach (Active active in mActiveList)
+ {
+ if (active.Changed)
+ list.Add(active);
+ }
+ }
+ else if (mBegun && mPrepared)
+ {
+ if (mChanged)
+ {
+ // Find all the changed activations and add them to the list
+ foreach (Active active in mActiveList)
+ {
+ if (active.Changed)
+ list.Add(active);
+ }
+ }
+ }
+
+ return list;
+ }
+ }
}