#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);
}
}
}