#region Copyright /* * Copyright (c) 2005,2006,2007, OpenMI Association * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the OpenMI Association nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY "OpenMI Association" ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL "OpenMI Association" BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #endregion using System; using System.Collections; using System.Runtime.Remoting; using OpenMI.Standard; using RTCTools.OpenMI.Sdk.Backbone; using RTCTools.OpenMI.Sdk.DevelopmentSupport; namespace RTCTools.OpenMI.Sdk.Wrapper { /// /// The LinkableRunEngine implements the run time part of the ILinkableComponent interface. /// The remaining methods are implemented in the derived LinkableEngine class. There are /// historical reasons for splitting the functionality between the two classes. /// The LinkableRunEngine class and the LinkableEngine class could be merged, /// but for the time being these are keeps as they are in order to support backward compatibility. /// [Serializable] public abstract class LinkableRunEngine : LinkableComponent { //TODO: The elementset version number should be checked and the elementmapper //called in order to update the mapping a matrix when the version has changed /// /// List of SmartInputLinks /// protected ArrayList _smartInputLinks; /// /// List of SmartOutput Links /// protected ArrayList _smartOutputLinks; /// /// Reference to the engine. Must be assigned in the derived class /// protected IRunEngine _engineApiAccess; /// /// True if the _engineApiAccess was assigned /// protected bool _engineWasAssigned; /// /// True if the Initialize method was invoked /// protected bool _initializeWasInvoked; /// /// True if the Prepare method was invoked /// protected bool _prepareForCompotationWasInvoked; /// /// True if the component is gathering data from other LinkableComponents /// protected bool _isBusy; /// /// Arraylist of published event types /// protected ArrayList _publishedEventTypes; /// /// used when comparing time in the IsLater method (see property TimeEpsilon) /// protected double _timeEpsilon; // used when comparing time in the IsLater method (see property TimeEpsilon) /// /// Current validation string from the Validate method /// protected ArrayList _validationWarningMessages; /// /// The current validateion error message /// protected ArrayList _validationErrorMessages; /// /// Constructor method for the LinkableRunEngine class /// public LinkableRunEngine() { _engineWasAssigned = false; _initializeWasInvoked = false; _prepareForCompotationWasInvoked = false; _timeEpsilon = 0.10 * 1.0 / (3600.0 * 24.0); _publishedEventTypes = new ArrayList(); _publishedEventTypes.Add(EventType.DataChanged); _publishedEventTypes.Add(EventType.Informative); _publishedEventTypes.Add(EventType.SourceAfterGetValuesCall); _publishedEventTypes.Add(EventType.SourceBeforeGetValuesReturn); _publishedEventTypes.Add(EventType.TargetAfterGetValuesReturn); _publishedEventTypes.Add(EventType.TargetBeforeGetValuesCall); _validationWarningMessages = new ArrayList(); _validationErrorMessages = new ArrayList(); _smartInputLinks = new ArrayList(); _smartOutputLinks = new ArrayList(); } /// /// Implementation of the same method in the /// OpenMI.Standard.ILinkableComponent interface /// public override ITimeStamp EarliestInputTime { get { return (_engineApiAccess.GetEarliestNeededTime()); } } /// /// This _timeEpsilon variable is used when comparing the current time in the engine with /// the time specified in the parameters for the GetValue method. /// if ( requestedTime > engineTime + _timeEpsilon) then PerformTimestep().. /// The default values for _timeEpsilon is double.Epsilon = 4.94065645841247E-324 /// The default value may be too small for some engines, in which case the _timeEpsilon can /// be changed the class that you have inherited from LinkableRunEngine og LinkableEngine. /// public double TimeEpsilon { get { return _timeEpsilon; } set { _timeEpsilon = value; } } /// /// Add a link to the LinkableComponent /// /// The Link public override void AddLink(ILink newLink) { try { if (!_initializeWasInvoked) { throw new System.Exception("AddLink method in the SmartWrapper cannot be invoked before the Initialize method has been invoked"); } if (_prepareForCompotationWasInvoked) { throw new System.Exception("AddLink method in the SmartWrapper cannot be invoked after the PrepareForComputation method has been invoked"); } if(newLink.TargetComponent == this) { _smartInputLinks.Add (this.CreateInputLink(this._engineApiAccess, newLink)); } else if(newLink.SourceComponent == this) { this._smartOutputLinks.Add (this.CreateOutputLink(this._engineApiAccess, newLink)); } else { throw new System.Exception("SourceComponent.ID or TargetComponent.ID in Link does not match the Component ID for the component to which the Link was added"); } } catch (System.Exception e) { string message = "Exception in LinkableComponent. "; message += "ComponentID: " + this.ComponentID + "\n"; throw new System.Exception(message,e); } } /// /// Creates a new input link /// /// The engine /// The link /// The new input link public virtual SmartInputLink CreateInputLink(IRunEngine engine, ILink link) { return new SmartInputLink (engine, link); } /// /// Creates a new output link /// /// The engine /// The link /// The new output link public virtual SmartOutputLink CreateOutputLink(IRunEngine engine, ILink link) { SmartOutputLink smartOutputLink = new SmartOutputLink (engine, link); smartOutputLink.Initialize(); return smartOutputLink; } /// /// Implementaion of the same method in the /// OpenMI.Standard.ILinkableComponent /// public override void Dispose() { _engineApiAccess.Dispose(); } /// /// Implementation of the same method in /// OpenMI.Standard.ILInkableComponent /// /// Time (ITimeSpan or ITimeStamp) for which values are requested /// LinkID associated to the requested values /// The values public override IValueSet GetValues(ITime time, string LinkID) { try { CheckTimeArgumentInGetvaluesMethod(time); SendSourceAfterGetValuesCallEvent(time, LinkID); IValueSet engineResult = new ScalarSet(); int outputLinkIndex = -999; for (int i = 0; i < _smartOutputLinks.Count; i++) { if ( ((SmartOutputLink) _smartOutputLinks[i]).link.ID == LinkID) { outputLinkIndex = i; break; } } if (_isBusy==false) { //while(IsLater(time,_engineApiAccess.GetCurrentTime())) while(IsLater(time, ((SmartOutputLink) _smartOutputLinks[outputLinkIndex]).GetLastBufferedTime())) { _isBusy=true; //Update input links foreach(SmartInputLink smartInputLink in _smartInputLinks) { smartInputLink.UpdateInput(); } _isBusy=false; //Perform Timestep if(_engineApiAccess.PerformTimeStep()) { //Update buffer with engine values, Time is timestamp foreach (SmartOutputLink smartOutputLink in _smartOutputLinks) { smartOutputLink.UpdateBuffer(); } SendEvent(EventType.DataChanged); } } } engineResult = ((SmartOutputLink)_smartOutputLinks[outputLinkIndex]).GetValue(time); SendEvent(EventType.SourceBeforeGetValuesReturn); return engineResult; } catch (System.Exception e) { string message = "Exception in LinkableComponent. ComponentID: "; message += this.ComponentID; throw new System.Exception(message,e); } } /// /// Description of the component /// public override string ComponentDescription { get { return _engineApiAccess.GetComponentDescription(); } } /// /// ID for the component /// public override string ComponentID { get { if (_engineApiAccess != null) { return _engineApiAccess.GetComponentID(); } else { return null; } } } /// /// Finish /// public override void Finish() { _engineApiAccess.Finish(); } /// /// Initialize /// /// Initialization parameters public override void Initialize(IArgument[] properties) { System.Collections.Hashtable hashtable =new Hashtable(); for(int i = 0; i < properties.Length;i++) { hashtable.Add(properties[i].Key,properties[i].Value); } SetEngineApiAccess(); this._engineWasAssigned = true; _engineApiAccess.Initialize(hashtable); if (!_engineWasAssigned) { throw new System.Exception("The Initialize method in the SmartWrapper cannot be invoked before the EngineApiAccess is assigned" ); } _initializeWasInvoked = true; } /// /// Prepare. This method will be invoked after end of configuration and before the first GetValues call /// public override void Prepare() { try { if (!_engineWasAssigned) { throw new System.Exception("PrepareForComputation method in SmartWrapper cannot be invoked before the EngineApiAccess has been assigned"); } if (!_initializeWasInvoked) { throw new System.Exception("PrepareForComputation method in SmartWrapper cannot be invoked before the Initialize method has been invoked"); } Validate(); if (_validationErrorMessages.Count > 0) { string errorMessage = ""; foreach (string str in _validationErrorMessages) { errorMessage += "Error: " + str + ". "; } throw new Exception(errorMessage); } foreach (SmartOutputLink smartOutputLink in _smartOutputLinks) { smartOutputLink.UpdateBuffer(); } _prepareForCompotationWasInvoked = true; } catch (System.Exception e) { string message = "Exception in LinkableComponent. "; message += "ComponentID: " + this.ComponentID + "\n"; throw new System.Exception(message,e); } } /// /// Remove a link /// /// Link ID for the link to be removed public override void RemoveLink(string LinkID) { try { if (!_initializeWasInvoked) { throw new Exception("Illegal invocation of RemoveLink method before invocation of Initialize method"); } if (_prepareForCompotationWasInvoked) { throw new Exception("Illegal invocation of RemoveLink method after invocation of Prepare method"); } int index = -999; for (int i = 0; i < _smartInputLinks.Count; i++) { if (((SmartInputLink)_smartInputLinks[i]).link.ID == LinkID) { index = i; break; } } if (index != -999) { _smartInputLinks.RemoveAt(index); } else { for (int i = 0; i < _smartOutputLinks.Count; i++) { if(((SmartOutputLink) _smartOutputLinks[i]).link.ID == LinkID) { index = i; break; } } _smartOutputLinks.RemoveAt(index); } if (index == -999) { throw new Exception("Failed to find link.ID in internal link lists in method RemoveLink()"); } } catch (System.Exception e) { string message = "Exception in LinkableComponent. "; message += "ComponentID: " + this.ComponentID + "\n"; throw new System.Exception(message,e); } } /// /// Returns an array of input ILink which contains links already added to this component. /// /// Returns an array of ILink which contains links already added to this component public override ILink[] GetAcceptingLinks() { ArrayList links = new ArrayList(); foreach (SmartInputLink smartLink in _smartInputLinks) { links.Add (smartLink.link); } return (ILink[]) links.ToArray(typeof(ILink)); } /// /// Returns an array of output ILink which contains links already added to this component. /// /// Returns an array of output ILink which contains links already added to this component. public override ILink[] GetProvidingLinks() { ArrayList links = new ArrayList(); foreach (SmartOutputLink smartLink in _smartOutputLinks) { links.Add (smartLink.link); } return (ILink[]) links.ToArray(typeof(ILink)); } /// /// Get the reference to the engine /// public IRunEngine EngineApiAccess { get { return _engineApiAccess; } } /// /// Set reference to the engine /// protected abstract void SetEngineApiAccess(); /// /// Keep Curren state /// /// ID for the state keept public virtual string KeepCurrentState() { if (_engineApiAccess is IManageState) { string stateID; stateID = ((IManageState) _engineApiAccess).KeepCurrentState(); foreach (SmartOutputLink smartOutputLink in _smartOutputLinks) { smartOutputLink.KeepCurrentBufferState(stateID); } return stateID; } else { throw new Exception("KeepCurrentState was called but the engine does not implement IManageState"); } } /// /// Restore a state /// /// ID for the state to restore public virtual void RestoreState(string stateID) { if (_engineApiAccess is IManageState) { ((IManageState) _engineApiAccess).RestoreState(stateID); foreach (SmartOutputLink smartOutputLink in _smartOutputLinks) { smartOutputLink.RestoreBufferState(stateID); } } else { throw new Exception("RestoreState was called but the engine does not implement IManageState"); } } /// /// Clear a state /// /// ID for the state to clear public virtual void ClearState(string stateID) { if (_engineApiAccess is IManageState) { ((IManageState) _engineApiAccess).ClearState(stateID); foreach (SmartOutputLink smartOutputLink in _smartOutputLinks) { smartOutputLink.ClearBufferState(stateID); } } else { throw new Exception("ClearState was called but the engine does not implement IManageState"); } } /// /// Get the published event types. /// /// index for the requested event type /// the requested event type public override EventType GetPublishedEventType(int providedEventTypeIndex) { return (EventType) _publishedEventTypes[providedEventTypeIndex]; } /// /// Get the number of published event types /// /// Number of published event types public override int GetPublishedEventTypeCount() { return _publishedEventTypes.Count; } /// /// Convert a ITime object to a ITimeStamp. /// /// The ITime object to convert /// The converted time public static RTCTools.OpenMI.Sdk.Backbone.TimeStamp TimeToTimeStamp(ITime time) { RTCTools.OpenMI.Sdk.Backbone.TimeStamp t; if (time is ITimeStamp) { t = new RTCTools.OpenMI.Sdk.Backbone.TimeStamp(((ITimeStamp) time).ModifiedJulianDay); } else { t = new RTCTools.OpenMI.Sdk.Backbone.TimeStamp(((ITimeSpan) time).End.ModifiedJulianDay); } return t; } /// /// Will compare two times. If the first argument t1, is later than the second argument t2 /// the method will return true. Otherwise false will be returned. t1 and t2 can be of types /// ITimeSpan or ITimeStamp. /// /// First time /// Second time /// isLater protected bool IsLater(ITime t1, ITime t2) { double mt1, mt2; bool isLater = false; mt1 = TimeToTimeStamp(t1).ModifiedJulianDay; mt2 = TimeToTimeStamp(t2).ModifiedJulianDay; if (mt1 > mt2 + _timeEpsilon) { isLater = true; } else { isLater = false; } return isLater; } /// /// Converts a ITime object to a formatted string /// /// The time to convert /// The formatted string public static string ITimeToString(ITime time) { string timeString; if (time is ITimeStamp) { timeString = (CalendarConverter.ModifiedJulian2Gregorian(((ITimeStamp) time).ModifiedJulianDay)).ToString(); } else if (time is ITimeSpan) { timeString = "[" + (CalendarConverter.ModifiedJulian2Gregorian(((ITimeSpan) time).Start.ModifiedJulianDay)).ToString() + ", " + (CalendarConverter.ModifiedJulian2Gregorian(((ITimeSpan) time).End.ModifiedJulianDay)).ToString() + "]"; } else { throw new System.Exception("Illigal type used for time, must be OpenMI.Standard.ITimeStamp or OpenMI.Standard.TimeSpan"); } return timeString; } /// /// Model descscription /// public override abstract string ModelDescription { get; } /// /// Model ID /// public override abstract string ModelID { get; } /// /// Time Horizon /// public override abstract ITimeSpan TimeHorizon { get; } /// /// Number of input exchange items /// public override abstract int InputExchangeItemCount { get; } /// /// number of output exchange items /// public override abstract int OutputExchangeItemCount { get; } /// /// get an input exchange item /// /// index number for the requested input exchange item /// the requested input exchange item public override abstract IInputExchangeItem GetInputExchangeItem(int index); /// /// get an output exchange item. /// /// index number for the requested exchange item /// the requested exchange item public override abstract IOutputExchangeItem GetOutputExchangeItem(int index); private ArrayList GetAllLinks() { ArrayList links = new ArrayList(); foreach (SmartInputLink inputLink in _smartInputLinks) { links.Add(inputLink.link); } foreach (SmartOutputLink outputLink in _smartOutputLinks) { links.Add(outputLink.link); } return links; } /// /// Validate the component /// /// Empty string if no warnings were issued, or a description if there were warnings public override string Validate() { _validationErrorMessages.Clear(); _validationWarningMessages.Clear(); foreach (SmartLink link in _smartInputLinks) { _validationErrorMessages.AddRange (link.GetErrors()); _validationWarningMessages.AddRange (link.GetWarnings()); } foreach (SmartLink link in _smartOutputLinks) { _validationErrorMessages.AddRange (link.GetErrors()); _validationWarningMessages.AddRange (link.GetWarnings()); } string validationString = ""; foreach (string str in _validationErrorMessages) { validationString += "Error: " + str + " "; } foreach (string str in _validationWarningMessages) { validationString += "Warning: " + str + ". "; } return validationString; } private void CheckTimeArgumentInGetvaluesMethod(ITime time) { if (time is ITimeSpan) { if (this._engineApiAccess is IEngine) { if (IsLater(((IEngine)this._engineApiAccess).GetTimeHorizon().Start, ((ITimeSpan)time).Start)) { throw new Exception("GetValues method was invoked using a time argument that representes a time before the allowed time horizon"); } if (IsLater(((ITimeSpan)time).End, ((IEngine)this._engineApiAccess).GetTimeHorizon().End)) { throw new Exception("GetValues method was invoked using a time argument that representes a time that is after the allowed time horizon"); } } } else if (time is ITimeStamp) { if (this._engineApiAccess is IEngine) { if (IsLater(((IEngine)this._engineApiAccess).GetTimeHorizon().Start, (ITimeStamp)time)) { throw new Exception("GetValues method was invoked using a time argument that representes a time before the allowed time horizon"); } if (IsLater((ITimeStamp)time, ((IEngine)this._engineApiAccess).GetTimeHorizon().End)) { throw new Exception("GetValues method was invoked using a time argument that representes a time that is after the allowed time horizon"); } } } else { throw new Exception("Illegal data type for time was used in argument to GetValues method. Type must be OpenMI.Standard.ITimeStamp or ITimeSpan"); } } private void SendSourceAfterGetValuesCallEvent(ITime time, string LinkID) { RTCTools.OpenMI.Sdk.Backbone.Event eventA = new RTCTools.OpenMI.Sdk.Backbone.Event(EventType.SourceAfterGetValuesCall); eventA.Description = "GetValues(t = " + ITimeToString(time) + ", "; eventA.Description += "LinkID: " + LinkID; //TODO: QS = " + _smartOutputLinkSet.GetLink(LinkID).SourceQuantity.ID + " ,QT = " + _smartOutputLinkSet.GetLink(LinkID).TargetQuantity.ID; eventA.Description += ") <<<==="; eventA.Sender = this; eventA.SimulationTime = TimeToTimeStamp(_engineApiAccess.GetCurrentTime()); eventA.SetAttribute("GetValues time argument : ",ITimeToString(time)); SendEvent(eventA); } private void SendEvent( EventType eventType) { RTCTools.OpenMI.Sdk.Backbone.Event eventD = new RTCTools.OpenMI.Sdk.Backbone.Event(eventType); eventD.Description = eventType.ToString(); eventD.Sender = this; eventD.SimulationTime = TimeToTimeStamp(_engineApiAccess.GetCurrentTime()); SendEvent(eventD); } } }