Problem

  • Converting IValueSet results to enumerations

Proposal

Additions to Standard
interface IExchangeAdaptor
    {
        IExchangeItem ExchangeItemOriginal { set; get; }
        IExchangeItem ExchangeItemNew { get; }
        object Adapt(IValueSet value);
    }

    // Optional Extension for ILinkableComponent

    interface IExchangeAdaptable
    {
        int SuggestedAdaptorsCount { get; }
        IExchangeAdaptor AdaptorSugestion(int index);
    }
Optional change to Standard
    class ILinkableComponent
    {
        ......
        object GetValues(ITime time, string linkID);
        ......
    }

Example

Enumeration
    enum ReedGrowth { High = 0, Medium, Low, }
Example of it working
        static void Adaptors()
        {
            ITime time = new TimeStamp(10.0);
            string linkid = "wibble";

            // has an output exchange item which returns Reed Growth as a height (IScalar/double)
            ILinkableComponent iLC = new LinkableComponentReedGrowth();

            IValueSet vs = iLC.GetValues(time, linkid);

            double d = ((IScalarSet)vs).GetScalar(0);

            string id = iLC.GetOutputExchangeItem(0).Quantity.ID;
            string idd = iLC.GetOutputExchangeItem(0).Quantity.Description;

            // make 3rd party Linkable component adaptable
            LinkableComponentAdaptable iLCA = new LinkableComponentAdaptable(iLC);

            // change Reed Growth to an enum ReedGrowth { High = 0, Medium, Low, } 
            int index = iLCA.MakeAdaption("ReedGrowthHeight", new ReedGrowthEnum());

            ReedGrowth growth = (ReedGrowth)iLCA.GetValuesAdapted(time, linkid);

            string ida = iLCA.GetOutputExchangeItem(index).Quantity.ID;
            string idda = iLCA.GetOutputExchangeItem(index).Quantity.Description;

            // Another proposed change:
            // object GetValues(time, linkid, out bool adapted);
            // if adapted false then IValueSet returned, else user needs to test against 'as' ie ...

            object o = iLCA.GetValuesAdapted(time, linkid);

            if (o is ReedGrowth)
            {
                ReedGrowth gr = (ReedGrowth)o;

                bool badGrowth = gr == ReedGrowth.High;
            }
        }
Wrapped LinkableComponent to allow Adaption
    class LinkableComponentAdaptable : ILinkableComponent
    {
        // Redirect to aggregated linkiable component all members ....

        #region ILinkableComponent Members
        #region IPublisher Members

        // ... except ....

        public LinkableComponentAdaptable(ILinkableComponent iLC)
        {
            _iLC = iLC;
        }

        public int OutputExchangeItemCount
        {
            get { return _iLC.OutputExchangeItemCount; }
        }

        public IOutputExchangeItem GetOutputExchangeItem(int index)
        {
            IOutputExchangeItem item = _iLC.GetOutputExchangeItem(index);

            if (_adaptedExchangeItems.ContainsKey(item))
                return (IOutputExchangeItem)_adaptedExchangeItems[item].ExchangeItemNew;

            return item;
        }

        public IValueSet GetValues(ITime time, string linkID)
        {
            return _iLC.GetValues(time, linkID);
        }

        public object GetValuesAdapted(ITime time, string linkID)
        {
            IValueSet vs = _iLC.GetValues(time, linkID);

            /*
             * from linkid get iLink
             * from iLink get sourceComponent
             * from iLink get sourceQuantity.ID
             * fudged below ....
             */

            ILinkableComponent iSource = this; // fudge
            string sourceQID = "ReedGrowthHeightEnum"; // fudge

            int index = -1;

            for (int n = 0; n < iSource.OutputExchangeItemCount; ++n)
            {
                if (iSource.GetOutputExchangeItem(n).Quantity.ID == sourceQID)
                    index = n;
            }

            if (index > -1)
            {
                IOutputExchangeItem item = _iLC.GetOutputExchangeItem(index);

                if (_adaptedExchangeItems.ContainsKey(item))
                    return _adaptedExchangeItems[item].Adapt(vs);
            }

            return vs;
        }

        // New Members

        public int MakeAdaption(string quantityId, IExchangeAdaptor adaptor)
        {
            for (int n = 0; n < OutputExchangeItemCount; ++n)
            {
                if (GetOutputExchangeItem(n).Quantity.ID == quantityId)
                {
                    adaptor.ExchangeItemOriginal = GetOutputExchangeItem(n);
                    _adaptedExchangeItems.Add(GetOutputExchangeItem(n), adaptor);
                    return n;
                }
            }

            return -1;
        }

        Dictionary<IOutputExchangeItem, IExchangeAdaptor> _adaptedExchangeItems = new Dictionary<IOutputExchangeItem, IExchangeAdaptor>();
        ILinkableComponent _iLC;
    }

Quantity for Reed Growth Enumeration
    class QuantityAsEnum : IQuantity
    {
        IQuantity _quantity;

        public QuantityAsEnum(IQuantity q)
        {
            _quantity = q;
        }

        #region IQuantity Members

        public string ID
        {
            get { return _quantity.ID + "Enum"; }
        }

        public string Description
        {
            get { return _quantity.Description + " Adapted to enumeration return value"; }
        }

        public org.OpenMI.Standard.ValueType ValueType
        {
            get { return _quantity.ValueType; }
        }

        public IDimension Dimension
        {
            get { return _quantity.Dimension; }
        }

        public IUnit Unit
        {
            get { return _quantity.Unit; }
        }

        #endregion
    }
Adapter to make Reed Growth an enum
    class ReedGrowthEnum : IExchangeAdaptor
    {
        double _high = 15;
        double _low = 5;

        IExchangeItem _original;
        IExchangeItem _new;

        #region IAdaptor Members

        public object Adapt(IValueSet value)
        {
            ReedGrowth rg = ReedGrowth.Medium;

            double d = ((IScalarSet)value).GetScalar(0);

            if (d > _high)
                rg = ReedGrowth.High;

            if (d < _low)
                rg = ReedGrowth.Low;

            return rg;
        }

        public IExchangeItem ExchangeItemOriginal 
        {
            set 
            {
                _original = value;

                if (value is IInputExchangeItem)
                    _new = new InputExchangeItem(new QuantityAsEnum(_original.Quantity), _original.ElementSet);
                else if (value is IOutputExchangeItem)
                    _new = new OutputExchangeItem(new QuantityAsEnum(_original.Quantity), _original.ElementSet);
                else
                {
                    Debug.Assert(false);
                }
            }

            get { return _original; }
        }

        public IExchangeItem ExchangeItemNew
        {
            get { return _new; }
        }

        #endregion
    }
Original LinkableComponent with Reed Growth as a double
    class LinkableComponentReedGrowth : ILinkableComponent
    {
        #region ILinkableComponent Members
        #region IPublisher Members

        public int OutputExchangeItemCount
        {
            get { return 1; }
        }

        public IOutputExchangeItem GetOutputExchangeItem(int index)
        {
            return index == 0 ? _reeds : null;
        }

        public IValueSet GetValues(ITime time, string linkID)
        {
            return new Double(12);
        }

        ReedGrowthHeight _reeds = new ReedGrowthHeight();
     }
Reed growth as double Output Exchange Item
    class ReedGrowthHeight : IOutputExchangeItem
    {
        #region IOutputExchangeItem Members

        #region IExchangeItem Members

        public IQuantity Quantity
        {
            get { return _quantity; }
        }

        public IElementSet ElementSet
        {
            get { return null; }
        }

        #endregion

        ReedGrowthQuantity _quantity = new ReedGrowthQuantity();
    }
Reed Growth as double Quantity
    class ReedGrowthQuantity : IQuantity
    {
        #region IQuantity Members

        public string ID
        {
            get { return "ReedGrowthHeight"; }
        }

        public string Description
        {
            get { return "ReedGrowthHeight as a scalor"; }
        }

        public org.OpenMI.Standard.ValueType ValueType
        {
            get { return org.OpenMI.Standard.ValueType.Scalar; }
        }

        public IDimension Dimension
        {
            get { return null; }
        }

        public IUnit Unit
        {
            get { return null; }
        }

        #endregion
    }
Util to give me a IScalerSet from a double
    class Double : IScalarSet
    {
        double _double;

        public Double(double d)
        {
            _double = d;
        }

        #region IScalarSet Members

        public double GetScalar(int index)
        {
            return IsValid(index) ? _double : -999.999;
        }

        #endregion

        #region IValueSet Members

        public int Count
        {
            get { return 1; }
        }

        public bool IsValid(int index)
        {
            return index == 0;
        }

        #endregion
    }

  • No labels

1 Comment

  1. Unknown User (don)

    Looks complicated.

    • QuantityAsEnum does not say anything about enum except returning it in ID.
    • It looks like in the above example ExchangeItems being converted and not values.
    • Introducing a new separate type (IExchangeAdapter) will make standard more complicated and does not seem to be very intuitive.

    How I'd expect it to be used when it is necessary to convert values and not exchange items. For example to convert values from exchange item containing value as a string to value in exchange item wrapping enum-type values:

    class StringValueTypeExchangeItem : IExchangeItem
    {
       String v;
    
       object Value
       { 
          get { return v; } 
          set { v = (String)value; } 
       } 
    }
    
    // this class returns value from connected exchange item if it is defined (provider).
    class EnumValueTypeExchangeItem : IExchangeItem
    {
       IExchangeItem provider; // not null if this exchange item is connected somewhere
    
       MyCustomEnum v;
    
       // returns value from connected exchange item if it is defined
       public object Value
       { 
          get 
          { 
             if(provider!= null) 
             {
                return provider.Value; 
             }
             else
             {
                return v;
             }
          }
     
          set { ... } 
       } 
    }
    
    class ValueTransformationTests
    {
       void TransformExchangeItemValuesFromStringToEnum()
       {  
          // get these exchange items somewhere
          StringValueTypeExchangeItem src = ...;
          EnumValueTypeExchangeItem dst = ...; 
    
          // make sure value types in Quantities are ok
          Assert.AreEqual(src.Quantity.ValueType, typeof(String));
          Assert.AreEqual(dst.Quantity.ValueType, typeof(MyCustomEnum));
    
          /*
             Create component containing 2 exchange items:
                   src => InputExchangeItem ---<> ValueConvertorComponent <>--- OutputExchangeItem => dst
          */
    
          IComponent valueConvertor = ...; 
    
          valueConvertor.InputItem[0].ConnectFrom(src);
          valueConvertor.OutputItem[0].ConnectTo(dst);
    
          // here valueConvertor component will return value converted from string to enum!
          // user will not notice it, everything will happen automatically on-the-fly
          MyCustomEnum value = (MyCustomEnum)dst.Value; 
       }
    }