using System; using System.Collections.Generic; namespace SCMA.AdapterCom { // Oggetto SEMPLICE x definizione data item... con MTC verrà fatta conversione 1:1 ai dataitem di tipo MTC (con relative estensioni) public class SimpleDataItem { /// /// The name of the data item /// protected String mName; /// /// The value of the data item, can be any type. /// protected object mValue = "UNAVAILABLE"; /// /// A flag to indicate if the data item's value has changed since it /// has last been set. /// protected bool mChanged = true; /// /// An indicator that this data item must be sent on a separate line. /// This is done for all data items that are more complex than simple /// Key|Value pairs. /// protected bool mNewLine = false; /// /// Optional device prefix. /// public string DevicePrefix = null; /// /// Create a new data item /// /// The name of the data item public SimpleDataItem(String name) { mName = name; } /// /// Get and set the Value property. This will check if the value has changed /// and set the changed flag appropriately. Automatically boxes types so will /// work for any data. /// public object Value { set { if (!mValue.Equals(value)) { mValue = value; mChanged = true; } } get { return mValue; } } /// /// Make this data item unavailable. /// public virtual void Unavailable() { Value = "UNAVAILABLE"; } /// /// Checks if the data item is unavailable. /// /// true if Unavailable public bool IsUnavailable() { return mValue.Equals("UNAVAILABLE"); } /// /// Getter for the mChanged property. /// public bool Changed { get { return mChanged; } } /// /// Getter for the mNewLine property. /// public bool NewLine { get { return mNewLine; } } public void ForceChanged() { mChanged = true; } /// /// Simple string representation with pipe delim. /// /// A text representation public override string ToString() { if (DevicePrefix == null) return mName + "|" + mValue; else return DevicePrefix + ":" + mName + "|" + mValue; } /// /// These methods are mainly for conditions. They allow for /// mark and sweep of the condition activations. /// public virtual void Begin() { } public virtual void Prepare() { } /// /// Reset the Changed flag. /// public virtual void Cleanup() { mChanged = false; } /// /// Get a list of all the changed data items. Since this is a /// single value, just return a list with one item if it has /// changed /// /// true means to return this data item regardless of the /// changed flag. This is used to send initial data back to a new client. /// The changed data item public virtual List ItemList(bool all = false) { List list = new List(); if (all || mChanged) list.Add(this); return list; } #if false /// /// Event is just an alias for DataItem /// public class Event : DataItem { public Event(string name) : base(name) { } } /// /// A sample is a data item with a floating point value /// public class Sample : DataItem { public Sample(string name) : base(name) { } } /// /// A message is an event with an additional native code. The /// text of the message is the value. /// public class Message : DataItem { string mCode; /// /// Create a new message, set NewLine to true so this comes out /// on a separate line. /// /// The name of the data item public Message(string name) : base(name) { mNewLine = true; } /// /// Code property. /// public string Code { set { if (mCode != value) { mChanged = true; mCode = value; } } get { return mCode; } } /// /// The text representation of the code. /// /// A text representation public override string ToString() { return mName + "|" + mCode + "|" + mValue; } } /// /// 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 Condition : DataItem { /// /// 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 : DataItem { // 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; mNewLine = true; 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 Condition(String name, bool simple = false) : base(name) { mNewLine = true; 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; } } public class TimeSeries : DataItem { public double Rate { set; get; } public double[] mValues; public double[] Values { set { mValues = value; mChanged = true; } get { return mValues; } } public TimeSeries(string name, double rate = 0.0) : base(name) { mNewLine = true; Rate = rate; } /// /// Simple string representation with pipe delim. /// /// A text representation public override string ToString() { string rate = Rate == 0.0 ? "" : Rate.ToString(); string v; int count; if (mValues != null) { v = String.Join(" ", Values.Select(p => p.ToString()).ToArray()); count = Values.Count(); } else { count = 0; v = ""; } return mName + "|" + Values.Count().ToString() + "|" + rate + "|" + v; } } #endif } }