Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
using System; using System.Run-time.InteropServices;
 using System.Text;
 namespace MyOrganisation.OpenMI.MyModel
 {
     public class MyEngineDLLAccess
     {
          \[DllImport(@"Oatc.OpenMI.Examples.ModelComponents.SimpleRiver.Engine.dll",
 	       EntryPoint = "INITIALIZE",
 	       SetLastError=true,
 	       ExactSpelling = true,
 	       CallingConvention=CallingConvention.Cdecl)\]
 	       public static extern bool Initialize(string filePath, uint length);

            \[DllImport(@"Oatc.OpenMI.Examples.ModelComponents.SimpleRiver.Engine.dll",
 		EntryPoint = "PERFORMTIMESTEP",
 		SetLastError=true,
 		ExactSpelling = true,
 		CallingConvention=CallingConvention.Cdecl)\]
 		public static extern bool PerformTimeStep();

            \[DllImport(@"Oatc.OpenMI.Examples.ModelComponents.SimpleRiver.Engine.dll",
 	       EntryPoint = "FINISH",
 	       SetLastError=true,
 	       ExactSpelling = true,
 	       CallingConvention=CallingConvention.Cdecl)\]
 	       public static extern bool Finish();
     }
 }

4.4 Step 4: Implementing the MyEngineDotNetAccess

...


The normal convention for Fortran DLLs is that values are returned through the function (effectively, they are passed by reference); for C# results are passed by value. Therefore there may be a need to state explicitly that C# parameters passed to a Fortran routine are to be passed by reference. In Fortran, arrays normally start from index 1 whereas in C# the convention is to start from zero. These differences can be handled in the MyEngineDotNetAccess class. When you have completed the implementation of the methods shown below you can create and implement the corresponding test class and run the unit test.

Code Block
using System;
 using System.Text;
 namespace MyOrganisation.OpenMI.MyModel
 {
    public class MyEngineDotNetAccess
    {
       public void Initialize(string filePath)
       {
         if(\!(MyModelDLL.Initialize(filePath, ((uint) filePath.Length))))
         {
           CreateAndThrowException();
         }
       }
       public void PerformTimeStep()
       {
         if(\!(MyModelDLL.PerformTimeStep()))
         {
            CreateAndThrowException();
         }
       }
       public void Finish()
       {
          if(\!(MyModel.Finish()))
          {
              CreateAndThrowException();
          }
       }
       private void CreateAndThrowException()
       {
          int numberOfMessages = 0;
          numberOfMessages = MyModelDLL.GetNumberOfMessages();
          string message = 'Error Message from MyModel ';
          for (int i = 0; i < numberOfMessages; i++)
          {
             int n = i;
             StringBuilder messageFromCore = new StringBuilder(' ');
             MyModelDLL.GetMessage(ref n, messageFromCore, (uint) messageFromCore.Length);
             message \+='; ';
             message \+= messageFromCore.ToString().Trim();
          }
          throw new Exception(message);
       }
 }

4.5 Step 5: Implementing the MyEngineWrapper class

...


Example code for this is shown below.

Code Block
using System;
 using System.Collections;
 namespace MyOrganisation.OpenMI.MyModel
 {
     public class MyEngineWrapper : Oatc.OpenMI.Sdk.Wrapper.IEngine
     {   
         private MyEngineDotNetAccess \_myEngine;
         public void Initialize(Hashtable properties)
         { 
             \_myEngine = new MyEngineDotNetAccess();
             \_myEngine.Initialize((string)properties\["FilePath"\]);
         }
         public void Finish()         
        {
             \_simpleRiverEngine.Finish();
         }
     }
 }

4.6 Step 6: Implementing the MyModelLinkablComponent

...

Code Block
//== The org.OpenMI.Utilities.Wrapper.IEngine interface == // \-\\- Execution control methods (Inherited from IRunEngine) \-\\- void Initialize(Hashtable properties); bool PerformTimeStep(); void Finish(); //-\\- Time methods (Inherited from IRunEngine) \-\\- ITime GetCurrentTime(); ITime GetInputTime(string QuantityID, string ElementSetID); ITimeStamp GetEarliestNeededTime(); //-\\- Data access methods (Inherited from IRunEngine) \-\\- void SetValues(string QuantityID, string ElementSetID, IValueSet values); IValueSet GetValues(string QuantityID, string ElementSetID); //-\\- Component description methods (Inherited from IRunEngine) \-\\- double GetMissingValueDefinition(); string GetComponentID(); string GetComponentDescription(); // \-\\- Model description methods \-\\- string GetModelID(); string GetModelDescription(); double GetTimeHorizon(); // \-\\- Exchange items \-\\- int GetInputExchangeItemCount(); int GetOutputExchangeItemCount(); org.OpenMI.Backbone GetInputExchangeItem(int exchangeItemIndex); org.OpenMI.Backbone GetOutputExchangeItem(int exchangeItemIndex);

...

Code Block
public void Initialize(System.Collections.Hashtable properties) {    \_inputExchangeItems = new ArrayList(); //ArrayList of    Oatc.OpenMI.Sdk.Backbone.InputExchangeItem objects    \_outputExchangeItems = new ArrayList(); //ArrayList of    Oatc.OpenMI.Sdk.Backbone.OutputExchangeItem objects    // \-\\- Create and initialize the engine \-\\-    \_simpleRiverEngine = new SimpleRiverEngineDotNetAccess();    \_simpleRiverEngine.Initialize((string)properties\['FilePath'\]);    // \-\\- Simulation start time -    // The start time is obtained from the engine core as a string. This string is    // passed and converted to a System.DateTime. Then the    // Oatc.OpenMI.Sdk.DevelopmentSupport.CalendarConverter class is used to convert    // this time into the ModifiedJulianDay (this is the OpenMI standard time)    char \[\] delimiter = new char\[\]{'-',' ',':'};    string\[\] strings = \_simpleRiverEngine.GetSimulationStartDate().Split(delimiter);    int StartYear = Convert.ToInt32(strings\[0\]);    int StartMonth = Convert.ToInt32(strings\[1\]);    int StartDay = Convert.ToInt32(strings\[2\]);    int StartHour = Convert.ToInt32(strings\[3\]);    int StartMinute = Convert.ToInt32(strings\[4\]);    int StartSecond = Convert.ToInt32(strings\[5\]);    DateTime startDate = new DateTime(StartYear,StartMonth,StartDay,StartHour,StartMinute,StartSecond);    \_simulationStartTime = Oatc.OpenMI.Sdk.DevelopmentSupport.CalendarConverter.Gregorian2ModifiedJulian(startDate);    // \-\\- Build exchange items \--\\-    Dimension flowDimension = new Dimension();    Unit flowUnit = new Unit('m3/sec',1,0,'m3/sec'); //The Simple River only uses    // quantities with the unit m3/sec.    Quantity flowQuantity = new Quantity(flowUnit,'description','Flow',    OpenMI.Standard.ValueType.Scalar,flowDimension);    Quantity inFlowQuantity = new Quantity(flowUnit,'description','InFlow',    OpenMI.Standard.ValueType.Scalar,flowDimension);    int numberOfNodes = \_simpleRiverEngine.GetNumberOfNodes();    for (int i = 0; i < numberOfNodes \-1; i++) //For each branch    {       OutputExchangeItem flowFromBranch = new OutputExchangeItem();       InputExchangeItem inFlowToBranch = new InputExchangeItem();       // One ElementSet is created for each branch. The ElementID's are       // Branch:<Branch number>. E.g. 'Branch:3'       ElementSet branch = new ElementSet('description','Branch:' + i.ToString(),ElementType.XYPolyLine,new SpatialReference('ref'));       branch.AddElement(new Element('Branch:' + i.ToString()));       branch.Elements\[0\].AddVertex(new Vertex(_simpleRiverEngine.       GetXCo-ordinate(i),_simpleRiverEngine.GetYCo-ordinate(i),0));       branch.Elements\[0\].AddVertex(new Vertex(_simpleRiverEngine.       GetXCo-ordinate(i+1),_simpleRiverEngine.GetYCo-ordinate(i+1),0));       flowFromBranch.ElementSet = branch;       flowFromBranch.Quantity = flowQuantity;       inFlowToBranch.ElementSet = branch;       inFlowToBranch.Quantity = inFlowQuantity;       \_outputExchangeItems.Add(flowFromBranch);       \_inputExchangeItems.Add(inFlowToBranch);    }    for (int i = 0; i < numberOfNodes; i++) //For all nodes    {       InputExchangeItem inflowToNode = new InputExchangeItem();       // Each node is a ID-based ElementSet. The ElementSet ID are       // Node:<node number>. E.g. 'Node:3'       ElementSet node = new ElementSet('description','Node:' +       i.ToString(),ElementType.IDBased,new SpatialReference('ref'));       node.AddElement(new Element('Node:' + i.ToString()));       inflowToNode.Quantity = inFlowQuantity;       inflowToNode.ElementSet = node;      \_inputExchangeItems.Add(inflowToNode);    }    ElementSet Branches = new ElementSet('description','AllBranches',    ElementType.XYPolyLine,new SpatialReference('ref'));    for (int i = 0; i < numberOfNodes - 1;i++) //Create an InputExchangeItem that    // has all branches in one ElementSet    {       Element branch = new Element('Branch: ' + i.ToString());       branch.AddVertex(new Vertex(_simpleRiverEngine.       GetXCo-ordinate(i),_simpleRiverEngine.GetYCo-ordinate(i),0));       branch.AddVertex(new Vertex(_simpleRiverEngine.       GetXCo-ordinate(i+1),_simpleRiverEngine.GetYCo-ordinate(i+1),0));       Branches.AddElement(branch);    }    InputExchangeItem inFlowToBranches = new InputExchangeItem();    inFlowToBranches.ElementSet = Branches;    inFlowToBranches.Quantity = inFlowQuantity;    \_inputExchangeItems.Add(inFlowToBranches); }

...