Skip to content
Snippets Groups Projects
Commit c6486fee authored by Tim Übelhör's avatar Tim Übelhör
Browse files

Initial commit

parents
Branches
Tags
No related merge requests found
Showing
with 2660 additions and 0 deletions
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">$(MSBuildProjectDirectory)\..\</SolutionDir>
<!-- Enable the restore command to run before builds -->
<RestorePackages Condition=" '$(RestorePackages)' == '' ">false</RestorePackages>
<!-- Property that enables building a package from a project -->
<BuildPackage Condition=" '$(BuildPackage)' == '' ">false</BuildPackage>
<!-- Determines if package restore consent is required to restore packages -->
<RequireRestoreConsent Condition=" '$(RequireRestoreConsent)' != 'false' ">true</RequireRestoreConsent>
<!-- Download NuGet.exe if it does not already exist -->
<DownloadNuGetExe Condition=" '$(DownloadNuGetExe)' == '' ">true</DownloadNuGetExe>
</PropertyGroup>
<ItemGroup Condition=" '$(PackageSources)' == '' ">
<!-- Package sources used to restore packages. By default, registered sources under %APPDATA%\NuGet\NuGet.Config will be used -->
<!-- The official NuGet package source (https://www.nuget.org/api/v2/) will be excluded if package sources are specified and it does not appear in the list -->
<!--
<PackageSource Include="https://www.nuget.org/api/v2/" />
<PackageSource Include="https://my-nuget-source/nuget/" />
-->
</ItemGroup>
<PropertyGroup Condition=" '$(OS)' == 'Windows_NT'">
<!-- Windows specific commands -->
<NuGetToolsPath>$([System.IO.Path]::Combine($(SolutionDir), ".nuget"))</NuGetToolsPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(OS)' != 'Windows_NT'">
<!-- We need to launch nuget.exe with the mono command if we're not on windows -->
<NuGetToolsPath>$(SolutionDir).nuget</NuGetToolsPath>
</PropertyGroup>
<PropertyGroup>
<PackagesProjectConfig Condition=" '$(OS)' == 'Windows_NT'">$(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config</PackagesProjectConfig>
<PackagesProjectConfig Condition=" '$(OS)' != 'Windows_NT'">$(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config</PackagesProjectConfig>
</PropertyGroup>
<PropertyGroup>
<PackagesConfig Condition="Exists('$(MSBuildProjectDirectory)\packages.config')">$(MSBuildProjectDirectory)\packages.config</PackagesConfig>
<PackagesConfig Condition="Exists('$(PackagesProjectConfig)')">$(PackagesProjectConfig)</PackagesConfig>
</PropertyGroup>
<PropertyGroup>
<!-- NuGet command -->
<NuGetExePath Condition=" '$(NuGetExePath)' == '' ">$(NuGetToolsPath)\NuGet.exe</NuGetExePath>
<PackageSources Condition=" $(PackageSources) == '' ">@(PackageSource)</PackageSources>
<NuGetCommand Condition=" '$(OS)' == 'Windows_NT'">"$(NuGetExePath)"</NuGetCommand>
<NuGetCommand Condition=" '$(OS)' != 'Windows_NT' ">mono --runtime=v4.0.30319 "$(NuGetExePath)"</NuGetCommand>
<PackageOutputDir Condition="$(PackageOutputDir) == ''">$(TargetDir.Trim('\\'))</PackageOutputDir>
<RequireConsentSwitch Condition=" $(RequireRestoreConsent) == 'true' ">-RequireConsent</RequireConsentSwitch>
<NonInteractiveSwitch Condition=" '$(VisualStudioVersion)' != '' AND '$(OS)' == 'Windows_NT' ">-NonInteractive</NonInteractiveSwitch>
<PaddedSolutionDir Condition=" '$(OS)' == 'Windows_NT'">"$(SolutionDir) "</PaddedSolutionDir>
<PaddedSolutionDir Condition=" '$(OS)' != 'Windows_NT' ">"$(SolutionDir)"</PaddedSolutionDir>
<!-- Commands -->
<RestoreCommand>$(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)</RestoreCommand>
<BuildCommand>$(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols</BuildCommand>
<!-- We need to ensure packages are restored prior to assembly resolve -->
<BuildDependsOn Condition="$(RestorePackages) == 'true'">
RestorePackages;
$(BuildDependsOn);
</BuildDependsOn>
<!-- Make the build depend on restore packages -->
<BuildDependsOn Condition="$(BuildPackage) == 'true'">
$(BuildDependsOn);
BuildPackage;
</BuildDependsOn>
</PropertyGroup>
<Target Name="CheckPrerequisites">
<!-- Raise an error if we're unable to locate nuget.exe -->
<Error Condition="'$(DownloadNuGetExe)' != 'true' AND !Exists('$(NuGetExePath)')" Text="Unable to locate '$(NuGetExePath)'" />
<!--
Take advantage of MsBuild's build dependency tracking to make sure that we only ever download nuget.exe once.
This effectively acts as a lock that makes sure that the download operation will only happen once and all
parallel builds will have to wait for it to complete.
-->
<MsBuild Targets="_DownloadNuGet" Projects="$(MSBuildThisFileFullPath)" Properties="Configuration=NOT_IMPORTANT;DownloadNuGetExe=$(DownloadNuGetExe)" />
</Target>
<Target Name="_DownloadNuGet">
<DownloadNuGet OutputFilename="$(NuGetExePath)" Condition=" '$(DownloadNuGetExe)' == 'true' AND !Exists('$(NuGetExePath)')" />
</Target>
<Target Name="RestorePackages" DependsOnTargets="CheckPrerequisites">
<Exec Command="$(RestoreCommand)"
Condition="'$(OS)' != 'Windows_NT' And Exists('$(PackagesConfig)')" />
<Exec Command="$(RestoreCommand)"
LogStandardErrorAsError="true"
Condition="'$(OS)' == 'Windows_NT' And Exists('$(PackagesConfig)')" />
</Target>
<Target Name="BuildPackage" DependsOnTargets="CheckPrerequisites">
<Exec Command="$(BuildCommand)"
Condition=" '$(OS)' != 'Windows_NT' " />
<Exec Command="$(BuildCommand)"
LogStandardErrorAsError="true"
Condition=" '$(OS)' == 'Windows_NT' " />
</Target>
<UsingTask TaskName="DownloadNuGet" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<OutputFilename ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Reference Include="System.Core" />
<Using Namespace="System" />
<Using Namespace="System.IO" />
<Using Namespace="System.Net" />
<Using Namespace="Microsoft.Build.Framework" />
<Using Namespace="Microsoft.Build.Utilities" />
<Code Type="Fragment" Language="cs">
<![CDATA[
try {
OutputFilename = Path.GetFullPath(OutputFilename);
Log.LogMessage("Downloading latest version of NuGet.exe...");
WebClient webClient = new WebClient();
webClient.DownloadFile("https://www.nuget.org/nuget.exe", OutputFilename);
return true;
}
catch (Exception ex) {
Log.LogErrorFromException(ex);
return false;
}
]]>
</Code>
</Task>
</UsingTask>
</Project>
File added
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{BD7EDBDF-7A21-43C5-88A2-008D22B1BA84}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ModeliChart.DataSource</RootNamespace>
<AssemblyName>DataSource</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<PlatformTarget>AnyCPU</PlatformTarget>
<OutputPath>bin\Debug\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
<PlatformTarget>AnyCPU</PlatformTarget>
<OutputPath>bin\Release\</OutputPath>
</PropertyGroup>
<ItemGroup>
<Reference Include="OxyPlot, Version=1.0.0.0, Culture=neutral, PublicKeyToken=638079a8f0bd61e9, processorArchitecture=MSIL">
<HintPath>..\packages\OxyPlot.Core.1.0.0\lib\net45\OxyPlot.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="DataSourceBase.cs" />
<Compile Include="DataSourceManager.cs" />
<Compile Include="FMUDataSource\FMUBackend.cs" />
<Compile Include="FMUDataSource\FMUDataSource.cs" />
<Compile Include="FMUDataSource\FMUDataSourceFactory.cs" />
<Compile Include="FMUDataSource\MicroLibrary.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RIODataSource\RIODataSource.cs" />
<Compile Include="RIODataSource\RIODataSourceFactory.cs" />
<Compile Include="RIODataSource\RIOTCPController.cs" />
<Compile Include="TCPDataSource\NetworkProtocol.cs" />
<Compile Include="TCPDataSource\TCPDataSource.cs" />
<Compile Include="TCPDataSource\TCPDataSourceFactory.cs" />
<Compile Include="TCPDataSource\TCPMaster.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FMUDataSource\FMUDllWrapper\FMUDllWrapper.csproj">
<Project>{7bc0859f-25a7-4df7-8e5c-13c9da57ed6e}</Project>
<Name>FMUDllWrapper</Name>
</ProjectReference>
<ProjectReference Include="..\ModeliChartBasics\ModeliChartBasics.csproj">
<Project>{898aaee3-71c3-4047-a98b-0e74bbd97396}</Project>
<Name>ModeliChartBasics</Name>
</ProjectReference>
<ProjectReference Include="..\ModeliChartLog\ModeliChartLog.csproj">
<Project>{090bcfee-612d-43d9-95e4-7783ab5ddc2d}</Project>
<Name>ModeliChartLog</Name>
</ProjectReference>
<ProjectReference Include="..\ModeliChartSettings\ModeliChartSettings.csproj">
<Project>{7a46ec06-4eae-4d10-9cac-48bc730de548}</Project>
<Name>ModeliChartSettings</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
\ No newline at end of file
using ModeliChart.Basics;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
namespace ModeliChart.DataSource
{
// Serves for Basic information, most methods remain to be implemented
public abstract class DataSourceBase : IDataSource
{
protected string _instanceName;
protected string _location;
private ObservableCollection<IChannel> _channels; // Will notify controls on changes
protected Dictionary<string, IChannel> _channelByName; // Assemble the channels to this collections for faster getChannelByNames
protected ILookup<uint, IChannel> _channelByValueRef; // Be able to find the matching channels for a given valureRef
// Avoid the use of standard constructor
private DataSourceBase()
{
}
/// <summary>
/// Directly instantiates the DataSource by name and location
/// </summary>
/// <param name="instance"></param>
protected DataSourceBase(string instanceName, string location)
{
_instanceName = instanceName;
_location = location;
_channels = new ObservableCollection<IChannel>();
_channelByName = new Dictionary<string, IChannel>();
}
public string InstanceName
{
get { return _instanceName; }
}
public string Location
{
get { return _location; }
}
/// <summary>
/// Use this even in the sublasses to set and get a collection of Channels
/// </summary>
public ObservableCollection<IChannel> Channels
{
get
{
return _channels;
}
set
{
_channels = value;
_channelByName = _channels.ToDictionary(channel => channel.Name);
_channelByValueRef = _channels.ToLookup(channel => channel.ValueRef);
}
}
/// <summary>
/// Use an efficient collection like dictionary in background
/// Make it more efficient than scanning the ObservableCollection for the wanted channel
/// </summary>
/// <param name="valueRef"></param>
/// <returns></returns>
public IChannel getChannelByName(string name)
{
if (name != null && _channelByName.ContainsKey(name))
{
return _channelByName[name];
}
else
{
return null;
}
}
/// <summary>
/// The DataSource updates the samples and channels to the current time.
/// The backend must work in realtime.
/// </summary>
public abstract void updateValues();
/// <summary>
/// Simulate the values to the currentTime_sec
/// </summary>
/// <param name="currentTime_sec">Current time</param>
public abstract void doStep(double currentTime_sec);
/// <summary>
/// Sets one value for the valueRef
/// </summary>
/// <typeparam name="T">Type that should be set</typeparam>
/// <param name="valueRef">The unique reference of this value</param>
/// <param name="value">Value that will be set</param>
public abstract void setValue(uint valueRef, double value, ChannelType type);
// Even though DoStep is used by the controller to play or pause, these methods are needed for the state of the fmu
public abstract void play();
public abstract void pause();
public void stop()
{
// Let the backend stop
stopChild();
}
public abstract void stopChild();
#region IXmlSerializable Implementations currently not used, DataSourceSetup does the work
/*
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
while (reader.Read())
{
if (reader.IsStartElement())
{
switch (reader.Name)
{
case "Channel":
// Deserialize each channel
System.Xml.Serialization.XmlSerializer serializer = new XmlSerializer(typeof(Channel));
IChannel channel = serializer.Deserialize(reader.ReadSubtree()) as Channel;
_AvailableChannels.Add(channel);
break;
}
}
}
}
public void WriteXml(System.Xml.XmlWriter writer)
{
writer.WriteStartElement("Channels");
foreach (IChannel current in Channels)
{
// Serialize each Channel
System.Xml.Serialization.XmlSerializer serializer = new XmlSerializer(typeof(Channel));
serializer.Serialize(writer, current);
}
writer.WriteEndElement();
}
*/
#endregion
}
}
using ModeliChart.Basics;
using ModeliChart.Log;
using System.Collections.ObjectModel;
using System.Linq;
namespace ModeliChart.DataSource
{
/// <summary>
/// Helpes creating and managing DataSources
/// </summary>
public static class DataSourceManager
{
private static ObservableCollection<IDataSource> _dataSources;
public static ObservableCollection<IDataSource> DataSources
{
get { return _dataSources; }
set { _dataSources = value; }
}
/// <summary>
/// Creates and adds a new dataSource to the collection
/// Checks if the instanceName is unique
/// </summary>
/// <param name="type">The type of the new DataSource</param>
/// <param name="instanceName">The unique instanceName, will be checked if it is actually unique</param>
/// <param name="location">The path or endpoint of the dataSource</param>
/// <returns>The created DataSource, might be NULL.</returns>
public static IDataSource addDataSource(IDataSourceFactory factory, string instanceName, string location)
{
instanceName = uniqueDataSourceName(instanceName);
IDataSource res = factory.createDataSource(instanceName, location);
if (res != null)
{
_dataSources.Add(res);
}
return res;
}
/// <summary>
/// Removes the given dataSource
/// </summary>
/// <param name="dataSource"></param>
public static void removeDataSource(IDataSource dataSource)
{
// Removes the first item, since all dataSources must have a unique name only the right one will be removed
_dataSources.Remove(dataSource);
}
/// <summary>
/// Clears the collection and makes shure that the changed event is fired
/// </summary>
public static void removeAll()
{
// Remove one after each other, so the CollectionChanged event is raised
while(_dataSources.Count > 0)
{
_dataSources.RemoveAt(0);
}
}
/// <summary>
/// Handles renaming a dataSourceArea so each has a unique name.
/// If the name is already unique, the old name will be returned
/// </summary>
/// <param name="dataSource"></param>
/// <returns>A unique datasource name is returned.</returns>
public static string uniqueDataSourceName(string instanceName)
{
// Check if the Name already exists
while (_dataSources.Any(dataSource => dataSource.InstanceName.Equals(instanceName)))
{
instanceName = InputMessageBox.Show("The current DataSource is already in use. Please rename it!", "Rename DataSource", instanceName);
}
return instanceName;
}
/// <summary>
/// Find a dataSource by its name.
/// </summary>
/// <param name="instanceName">Will be searched for</param>
/// <returns>The first matching occurence or null if not found.</returns>
public static IDataSource findDataSource(string instanceName)
{
if(instanceName == null) { return null; }
// Find the match or null
return _dataSources.FirstOrDefault(source => source.InstanceName.Equals(instanceName));
}
/// <summary>
/// Find the dataSource for a channel
/// </summary>
/// <param name="channel">The DataSourceName member will be used for the search.</param>
/// <returns>The first matching occurence or null if not found</returns>
public static IDataSource findDataSource(IChannel channel)
{
return findDataSource(channel?.DataSourceName);
}
}
}
using FMUDllWrapper;
using ModeliChart.Basics;
using ModeliChart.Log;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace ModeliChart.DataSource
{
public enum FMUState
{
instantiated,
initializationMode,
slaveInitialized,
terminated,
undefined
}
public class FMUBackend
{
private FMUInstance _fmu;
private FMUState _state;
// Prevent changing the state during an operation (e.g. terminating while simulateValues)
private static readonly object _lockState = new object();
private String _instanceName;
private String _location;
// private System.Diagnostics.Stopwatch _sw; // For diagnostics only
// Collect the data here
private uint[] _realValueRefs;
private uint[] _intValueRefs;
private uint[] _boolValueRefs;
private double[] _realValues;
private int[] _intValues;
private bool[] _boolValues;
// Initial
private double ABSOLUTTIME = 0;
/* Diagnose times
long _DIAG_Total_US = 0;
long _DIAG_MinInterval_US;
long _DIAG_MaxInterval_US = 0;
int _DIAG_Iterations = 0;*/
const long _DIAG_ResetInterval_US = 5 * 1000 * 1000; // Reset the diagnose every 5s
const long TicksPerMicrosecond = 10;
// Hide the default constructor -> Only allow functional objects
private FMUBackend() { }
public FMUBackend(string instanceName, string location)
{
_state = FMUState.undefined;
// Remember the name and location
_location = location;
_instanceName = instanceName;
// Initialize
initialize();
// Create channels
storeValueRefs();
// Create StopWatch
// _sw = new System.Diagnostics.Stopwatch();
}
#region public Getter & Setter
public string Name
{
get { return _instanceName; }
}
public string FMUPath
{
get { return _location; }
}
public List<IChannel> getChannels(IDataSource dataSource)
{
// ♥ Dictionary
return FMUTools.createChannels(_fmu, dataSource);
}
/// <summary>
/// This is the order in which the data will be received
/// // real, int, bool
/// </summary>
public uint[] ValueRefs
{
get
{
var query = _realValueRefs.Concat(_intValueRefs).Concat(_boolValueRefs);
return query.ToArray();
}
}
#endregion
/// <summary>
/// Creates the FMUInstance, instantiates it and ends in initialization mode
/// </summary>
/// <param name="instanceName">Unique instance identifier</param>
/// <param name="location">Path to the .fmu file</param>
private void initialize()
{
// Do not interrupt this process
lock (_lockState)
{
// Check state
if (!(_state == FMUState.undefined || _state == FMUState.terminated))
{
// The fmu is already instantiated
return;
}
StatusLogger.Status = "Instantiating FMU";
// Check if the fmu file exists
if (!File.Exists(_location))
{
throw new FileNotFoundException("The given file location does not exist.");
}
if (!_location.EndsWith("fmu"))
{
throw new ArgumentException("The given file is not a fmu.");
}
// Create new Instance of FMU
_fmu = FMUInstance.fromFMU(_location);
StatusLogger.Status = "FMU instantiated";
// Create Logger Before loading so the Result can be logged
_fmu.LoggerMessageReceived += _fmu_LoggerMessageReceived;
// Load the files
DirectoryInfo path = new DirectoryInfo(_fmu.UnzipedPath);
Uri fmupath = new Uri(path.FullName);
ConsoleLogger.WriteLine("FMUBackend", "Start instantiation of FMI, Ver: " + _fmu.GetVersion());
// Instantiation and initialization
_fmu.Instantiate(_instanceName, fmupath.AbsoluteUri + "/resources", false, false);
// New State
_state = FMUState.instantiated;
_fmu.SetupExperiment(false, 0.0, 0.0, false, 10.0);
_fmu.EnterInitializationMode();
// New State
_state = FMUState.initializationMode;
StatusLogger.Status = "Initializing FMU (Create Channels)";
}
}
private void storeValueRefs()
{
// load the valuerefs & create the arrays for the values
var vr = FMUTools.sortValueRefs(_fmu);
_intValueRefs = vr.Item1;
_intValues = new int[_intValueRefs.Length];
_realValueRefs = vr.Item2;
_realValues = new double[_realValueRefs.Length];
_boolValueRefs = vr.Item3;
_boolValues = new bool[_boolValueRefs.Length];
}
/// <summary>
/// Releases all resources used by the FMU
/// </summary>
public void freeResources()
{
// Check if there is already an FMU loaded and dispose it
if (_fmu != null)
{
// Let the current operation finish
lock (_lockState)
{
_fmu.freeResources();
}
// Reset Times
ABSOLUTTIME = 0;
}
}
#region Play, Pause, Stop
public void Play()
{
// Only play if _fmu is Connected
if (_fmu != null)
{
lock (_lockState)
{
// Switching to play only possible from initMode
if (_state == FMUState.initializationMode)
{
_fmu.ExitInitializationMode();
_state = FMUState.slaveInitialized;
}else if(_state == FMUState.slaveInitialized)
{
// We are already in the right mode but that is no problem
}
else
{
sendLog("Playing is only possible from initializationMode, currentMode: " + _state.ToString());
}
}
}
// Not connected yet
else
{
// Show hint in the console
sendLog("Please connect to the DataSource first.");
}
}
public void Pause()
{
// Stay in simulationMode: slaveInitialized
// _sw.Stop();
}
/// <summary>
/// Stops the Simulation and resets the Simulation and Instruments
/// </summary>
public void Stop()
{
_state = FMUState.undefined;
// _sw.Stop();
// Reset the Simulation Time
ABSOLUTTIME = 0;
// Reset the Simulation
if (_fmu != null)
{
// TODO This should be used: _fmu.Reset(); Right now only dirty fix because fmiReset is not working in some FMUs.
lock (_lockState)
{
// Clear the fmu
freeResources();
_state = FMUState.undefined;
}
// Create new instance, do not lock here, because this method must change the state
initialize();
}
}
#endregion
public double[] simulateValues(double currentTime_sec)
{
#region Diagnostics: Get avg. times for simulation
/*
if (_sw.Elapsed.Ticks > 0)
{
// Convert to us, 1000 us/ms
long ticks = _sw.Elapsed.Ticks;
long us = ticks / TicksPerMicrosecond;
_DIAG_Total_US += us;
if (us < _DIAG_MinInterval_US)
{
_DIAG_MinInterval_US = us;
}
if (us > _DIAG_MaxInterval_US)
{
_DIAG_MaxInterval_US = us;
}
}
_sw.Stop();
_sw.Reset();
//Diagnostics
_sw.Start();
_DIAG_Iterations++;
// Show the diagnose and reset & restart the diagnose
if (_DIAG_Total_US > _DIAG_ResetInterval_US)
{
sendLog("Avg. interval: " + (((double)_DIAG_Total_US / (double)_DIAG_Iterations) * 1e-3).ToString() + "ms (Max: " + (double)_DIAG_MaxInterval_US * 1e-3 + "ms, Min: " + (double)_DIAG_MinInterval_US * 1e-3 + "ms)");
_DIAG_MinInterval_US = long.MaxValue;
_DIAG_MaxInterval_US = 0;
_DIAG_Total_US = 0;
_DIAG_Iterations = 0;
}
*/
#endregion Diagnostics
// Calculate the interval to get to the currentTime_sec
double interval_sec = currentTime_sec - ABSOLUTTIME;
// Do not interrput the step
lock (_lockState)
{
// only simulate if we are in the correct state
if (_state == FMUState.slaveInitialized)
{
// Compute the Simulation from absTime -> (absTime+Interval) = newAbsTime
_fmu.DoStep(ABSOLUTTIME, interval_sec, false);
// New CurrentPoint is currentTime_sec
ABSOLUTTIME = currentTime_sec;
// ABSOLUTTIME = Math.Round(ABSOLUTTIME, 5);
// Retrieve the Data and write them into arrays, this is quick!!!
// Do not assemble into a dictionary, only when the data gets pulled: getLatestSamples()
_realValues = _fmu.GetReal(_realValueRefs);
_intValues = _fmu.GetInteger(_intValueRefs);
_boolValues = _fmu.GetBoolean(_boolValueRefs);
// LINQ to create res vector
var query = _realValues.Concat(_intValues.Select(i => Convert.ToDouble(i)))
.Concat(_boolValues.Select(b => Convert.ToDouble(b)));
return query.ToArray();
}
else
{
return null;
}
}
}
/// <summary>
/// Sets the value in the FMUInstance
/// It is expected that the modelVariable for valueRef is settable
/// </summary>
/// <param name="valueRef">ValueReference from ModelDescription</param>
/// <param name="value">Value that will be set</param>
public void SetIntValue(uint valueRef, int value)
{
lock (_lockState)
{
// In these two states setting variables is allowed
if (_state == FMUState.initializationMode || _state == FMUState.slaveInitialized)
{
_fmu.SetInteger(new uint[] { valueRef }, new int[] { value });
}
}
}
/// <summary>
/// Sets the value in the FMUInstance
/// </summary>
/// <param name="valueRef">ValueReference from ModelDescription</param>
/// <param name="value">Value that will be set</param>
public void SetRealValue(uint valueRef, double value)
{
lock (_lockState)
{
// In these two states setting variables is allowed
if (_state == FMUState.initializationMode || _state == FMUState.slaveInitialized)
{
_fmu.SetReal(new uint[] { valueRef }, new double[] { value });
}
}
}
/// <summary>
/// Sets the value in the FMUInstance
/// </summary>
/// <param name="valueRef">ValueReference from ModelDescription</param>
/// <param name="value">Value that will be set</param>
public void SetBoolValue(uint valueRef, bool value)
{
lock (_lockState)
{
// In these two states setting variables is allowed
if (_state == FMUState.initializationMode || _state == FMUState.slaveInitialized)
{
_fmu.SetBoolean(new uint[] { valueRef }, new bool[] { value });
}
}
}
/// <summary>
/// Prints the message to the console
/// </summary>
/// <param name="message">The Message to be sent</param>
void _fmu_LoggerMessageReceived(string message)
{
//ignore this message:
//"CVODE: CVode failed with NONE:\n Internal t = 8 and h = 5.32907e-016 are such that t + h = t on the next step. The solver will continue anyway."
if (!message.EndsWith("The solver will continue anyway."))
{
sendLog("FMULog: " + message);
}
}
private void sendLog(string msg)
{
ConsoleLogger.WriteLine("FMUBackend", msg);
}
}
}
using ModeliChart.Basics;
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
namespace ModeliChart.DataSource
{
public class FMUDataSource : DataSourceBase
{
// The backend will do the simulation, the FMUDataSource only serves the IDataSource interface
private FMUBackend _backend;
private ValuesCache<double> _cache;
// know which order the data has
private uint[] _valueRefs;
public FMUDataSource(string instanceName, string location) : base(instanceName, location)
{
// Setup the backend
_backend = new FMUBackend(instanceName, location);
// Setup the channels for the dataSOurce
setupChannels();
}
~FMUDataSource()
{
// Make sure to clean up unmanaged code
_backend.freeResources();
}
private void setupChannels()
{
// Take over the values from the backend
_valueRefs = _backend.ValueRefs;
// Create cache
_cache = new ValuesCache<double>(_valueRefs.Length);
// Assemble the frontEnd Collections
Channels = new ObservableCollection<IChannel>(_backend.getChannels(this));
}
/// <summary>
/// The DataSource updates the samples and channels to the current time.
/// The backend must work in realtime.
/// </summary>
public override void updateValues()
{
lock (Channels)
{
// Receive the cache
var cache = _cache.getCache();
// Assemble the samples to the channels
Parallel.For(0, cache.Length, i =>
{
// get Channels
uint valueRef = _valueRefs[i];
var vrChannels = _channelByValueRef[valueRef];
// Add the dataPoints
foreach (IChannel current in vrChannels)
{
current.Add(cache[i]);
}
});
}
}
// Format of e: [time, _realValues..., _intValues]
public override void doStep(double currentTime_sec)
{
// Simulate to the currentTime
double[] res = _backend.simulateValues(currentTime_sec);
if (res != null)
{
// Copy the values to the cache
_cache.addPoint(currentTime_sec, res);
}
}
public override void setValue(uint valueRef, double value, ChannelType type)
{
switch (type)
{
case ChannelType.Integer:
_backend.SetIntValue(valueRef, (int)value);
break;
case ChannelType.Enum:
_backend.SetIntValue(valueRef, (int)value);
break;
case ChannelType.Real:
_backend.SetRealValue(valueRef, value);
break;
case ChannelType.Boolean:
_backend.SetBoolValue(valueRef, Convert.ToBoolean(value));
break;
}
}
// Be able to controll the simulation
public override void play()
{
_backend.Play();
}
public override void pause()
{
_backend.Pause();
}
public override void stopChild()
{
_backend.Stop();
_cache.resetCache();
}
}
}
using ModeliChart.Basics;
using ModeliChart.Log;
using System;
namespace ModeliChart.DataSource
{
public class FMUDataSourceFactory : IDataSourceFactory
{
public IDataSource createDataSource(string instanceName, string location)
{
// On fail an Error will be thrown
try
{
return new FMUDataSource(instanceName, location);
}
catch (Exception e)
{
ConsoleLogger.WriteLine("FMUDataSourceFactory", "FMUDataSource: " + e.Message);
StatusLogger.Reset();
return null;
}
}
}
}
using System;
namespace MicroLibrary
{
/// <summary>
/// MicroStopwatch class
/// </summary>
public class MicroStopwatch : System.Diagnostics.Stopwatch
{
readonly double _microSecPerTick =
1000000D / System.Diagnostics.Stopwatch.Frequency;
public MicroStopwatch()
{
if (!System.Diagnostics.Stopwatch.IsHighResolution)
{
throw new Exception("On this system the high-resolution " +
"performance counter is not available");
}
}
public long ElapsedMicroseconds
{
get
{
return (long)(ElapsedTicks * _microSecPerTick);
}
}
}
/// <summary>
/// MicroTimer class
/// </summary>
public class MicroTimer
{
public delegate void MicroTimerElapsedEventHandler(
object sender,
MicroTimerEventArgs timerEventArgs);
public event MicroTimerElapsedEventHandler MicroTimerElapsed;
System.Threading.Thread _threadTimer = null;
long _ignoreEventIfLateBy = long.MaxValue;
long _timerIntervalInMicroSec = 0;
bool _stopTimer = true;
public MicroTimer()
{
}
public MicroTimer(long timerIntervalInMicroseconds)
{
Interval = timerIntervalInMicroseconds;
}
public long Interval
{
get
{
return System.Threading.Interlocked.Read(
ref _timerIntervalInMicroSec);
}
set
{
System.Threading.Interlocked.Exchange(
ref _timerIntervalInMicroSec, value);
}
}
public long IgnoreEventIfLateBy
{
get
{
return System.Threading.Interlocked.Read(
ref _ignoreEventIfLateBy);
}
set
{
System.Threading.Interlocked.Exchange(
ref _ignoreEventIfLateBy, value <= 0 ? long.MaxValue : value);
}
}
public bool Enabled
{
set
{
if (value)
{
Start();
}
else
{
Stop();
}
}
get
{
return (_threadTimer != null && _threadTimer.IsAlive);
}
}
public void Start()
{
if (Enabled || Interval <= 0)
{
return;
}
_stopTimer = false;
System.Threading.ThreadStart threadStart = delegate()
{
NotificationTimer(ref _timerIntervalInMicroSec,
ref _ignoreEventIfLateBy,
ref _stopTimer);
};
_threadTimer = new System.Threading.Thread(threadStart);
_threadTimer.Name = "TimerThread";
_threadTimer.Priority = System.Threading.ThreadPriority.Normal;
_threadTimer.Start();
}
public void Stop()
{
_stopTimer = true;
}
public void StopAndWait()
{
StopAndWait(System.Threading.Timeout.Infinite);
}
public bool StopAndWait(int timeoutInMilliSec)
{
_stopTimer = true;
if (!Enabled || _threadTimer.ManagedThreadId ==
System.Threading.Thread.CurrentThread.ManagedThreadId)
{
return true;
}
return _threadTimer.Join(timeoutInMilliSec);
}
public void Abort()
{
_stopTimer = true;
if (Enabled)
{
_threadTimer.Abort();
}
}
void NotificationTimer(ref long timerIntervalInMicroSec,
ref long ignoreEventIfLateBy,
ref bool stopTimer)
{
int timerCount = 0;
long nextNotification = 0;
MicroStopwatch microStopwatch = new MicroStopwatch();
microStopwatch.Start();
while (!stopTimer)
{
long callbackFunctionExecutionTime =
microStopwatch.ElapsedMicroseconds - nextNotification;
long timerIntervalInMicroSecCurrent =
System.Threading.Interlocked.Read(ref timerIntervalInMicroSec);
long ignoreEventIfLateByCurrent =
System.Threading.Interlocked.Read(ref ignoreEventIfLateBy);
nextNotification += timerIntervalInMicroSecCurrent;
timerCount++;
long elapsedMicroseconds = 0;
while ( (elapsedMicroseconds = microStopwatch.ElapsedMicroseconds)
< nextNotification)
{
System.Threading.Thread.SpinWait(10);
}
long timerLateBy = elapsedMicroseconds - nextNotification;
if (timerLateBy >= ignoreEventIfLateByCurrent)
{
continue;
}
MicroTimerEventArgs microTimerEventArgs =
new MicroTimerEventArgs(timerCount,
elapsedMicroseconds,
timerLateBy,
callbackFunctionExecutionTime);
MicroTimerElapsed(this, microTimerEventArgs);
}
microStopwatch.Stop();
}
}
/// <summary>
/// MicroTimer Event Argument class
/// </summary>
public class MicroTimerEventArgs : EventArgs
{
// Simple counter, number times timed event (callback function) executed
public int TimerCount { get; private set; }
// Time when timed event was called since timer started
public long ElapsedMicroseconds { get; private set; }
// How late the timer was compared to when it should have been called
public long TimerLateBy { get; private set; }
// Time it took to execute previous call to callback function (OnTimedEvent)
public long CallbackFunctionExecutionTime { get; private set; }
public MicroTimerEventArgs(int timerCount,
long elapsedMicroseconds,
long timerLateBy,
long callbackFunctionExecutionTime)
{
TimerCount = timerCount;
ElapsedMicroseconds = elapsedMicroseconds;
TimerLateBy = timerLateBy;
CallbackFunctionExecutionTime = callbackFunctionExecutionTime;
}
}
}
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// Allgemeine Informationen über eine Assembly werden über die folgenden
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
// die einer Assembly zugeordnet sind.
[assembly: AssemblyTitle("DataSource")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("DataSource")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
[assembly: ComVisible(false)]
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
[assembly: Guid("bd7edbdf-7a21-43c5-88a2-008d22b1ba84")]
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
//
// Hauptversion
// Nebenversion
// Buildnummer
// Revision
//
// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
// übernehmen, indem Sie "*" eingeben:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
using ModeliChart.Basics;
using ModeliChart.Log;
using ModeliChart.Settings;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Net;
namespace ModeliChart.DataSource
{
public enum Rio_Input
{
A0,
A1,
A2,
A3,
B0,
B1,
B2,
B3,
C0,
C1,
Length // Trick, when getting this key -> int = Length
}
public enum Rio_Output
{
A0,
A1,
B0,
B1,
C0,
C1,
Length // Trick, when getting this key -> int = Length
}
public class RIODataSource : DataSourceBase
{
Dictionary<uint, Rio_Input> _inputsByRef; // Identify Inputs from external valueRef
Dictionary<uint, Rio_Output> _outputByRef; // Identify Outputs from external valueRef
Dictionary<Rio_Input, uint> _refByInput; // Identify a valueRef from the Input
RIO_TCPBackend _backend; // Controls the TCP connection
const int UPDATE_INTERVAL = 10; // In ms, default 10
ValuesCache<double> _cache; // [A0, A1,A2,...,C1] Length = 10
static readonly object _lock_LatestValues = new object(); // Lock when writing;
double[] _latestValues; // Store the samples from the RIO here before assembling them to the cache
const double DELTA_MAX_SEC = 10.0 / 1000; // Allow 10 ms difference between controller and RIO, then throw warning
public RIODataSource(string instanceName, string location)
: base(instanceName, location)
{
// Init variables
_outputByRef = new Dictionary<uint, Rio_Output>();
_inputsByRef = new Dictionary<uint, Rio_Input>();
_refByInput = new Dictionary<Rio_Input, uint>();
_channelByName = new Dictionary<string, IChannel>();
_backend = new RIO_TCPBackend();
// Cache has 11 Channels
_cache = new ValuesCache<double>((int)Rio_Input.Length);
// Subscribe backend event:
_backend.NewValuesArrived += _backend_NewValuesArrived;
// Fill dictionaries at the same time create channels
uint valueRef = 0; // Use this to create individual valueRefs
// Inputs
for (int i = 0; i < (int)Rio_Input.Length; i++)
{
// Increase valueRef
valueRef++;
// Cast the integer to the current Rio_Input
Rio_Input current = (Rio_Input)i;
// Add to Refs
_inputsByRef.Add(valueRef, current);
// Create the correlating Channel with the valueRef
string identifier = current.ToString(); // For Channel name
Channel c = new Channel("A-Input: " + identifier, "myRIO Analog Input: " + identifier, "Volt", valueRef, false, true, ChannelType.Real, this);
Channels.Add(c);
_channelByName.Add(c.Name, c);
}
// Outputs
for (int i = 0; i < (int)Rio_Output.Length; i++)
{
// Increase valueRef
valueRef++;
// Cast the integer to the current Rio_Output
Rio_Output current = (Rio_Output)i;
// Add to Refs
_outputByRef.Add(valueRef, current);
// Create the correlating Channel with the valueRef
string identifier = current.ToString(); // For Channel name
Channel c = new Channel("A-Output: " + identifier, "myRIO Analog Output: " + identifier, "Volt", valueRef, true, true, ChannelType.Real, null);
Channels.Add(c);
_channelByName.Add(c.Name, c);
}
// Let the Backend connect
instantiate(_location);
}
/// <summary>
/// Trys to connect to the instance, if NullOrWhiteSpace only defaultConnect
/// Also tries to connect to default
/// </summary>
/// <param name="location"></param>
/// <returns></returns>
private void instantiate(string location)
{
if (!String.IsNullOrWhiteSpace(location))
{
// If connection does not work try default connection
if (!Connect(location))
{
if (!ConnectDefault())
{
// We failed
throw new Exception("Could not connect to the RIO.");
}
}
}
// If we succeeded also set the interval so we can receive values
SetIntervalLength(UPDATE_INTERVAL);
}
// e: [Time, A0, A1,A2,...,C1] Length = 11
void _backend_NewValuesArrived(object sender, double[] e)
{
if (e.Length == 11)
{
lock (_lock_LatestValues)
{
_latestValues = e;
}
}
}
public override void doStep(double currentTime_sec)
{
lock (_lock_LatestValues)
{
// Compare the currentTime_sec and the RIOs time
double delta = _latestValues[0] - currentTime_sec;
if (Math.Abs(delta) > DELTA_MAX_SEC)
{
// only throw a warning
ConsoleLogger.WriteLine("RioDataSource", "Warning, difference between currentTime of RIO and controller: " + delta + "ms");
}
// Override the RIOs time with the one of the controller
_latestValues[0] = currentTime_sec;
// Extract values and add them to the cache
double[] values = new double[10];
Array.Copy(_latestValues, 1, values, 0, 10);
_cache.addPoint(currentTime_sec, values);
}
}
public override void updateValues()
{
// Clear the cache
var samples = _cache.getCache();
// The order of the samples equals the order of Rio_Input
for (int i = 0; i < (int)Rio_Input.Length; i++)
{
// Add the sample to the corresponding Channel, Channels-Order matches [A0, A1, ... , C1]
Channels[i].Add(samples[i]);
}
}
public override void setValue(uint valueRef, double value, ChannelType type)
{
if (Connected)
{
// The Rio only supports double, so no casting neccessary
_backend.SetOutputChannelValue(_outputByRef[valueRef], value);
}
}
#region play, pause, stop
/// <summary>
/// Plays with startTime = 0ms
/// </summary>
public override void play()
{
play(0);
}
/// <summary>
/// Tells the RIO to start playing and counting from startTime
/// </summary>
/// <param name="timeInterval">Time interval in ms</param>
public void play(double startTime)
{
_backend.StartInputChannelUpdates(startTime);
}
public override void pause()
{
stop();
_cache.resetCache();
}
public override void stopChild()
{
_backend.StopInputChannelUpdates();
}
#endregion
#region Connect & Disconnect methods
private bool ConnectDefault()
{
if (!_backend.Connected)
{
// Try to Connect to the default instances
//First try USB:
if (this.Connect("172.22.11.2:5000"))
{
_instanceName = "RIO-DataSource - USB";
_location = "172.22.11.2:5000";
return true;
}
//Second try WLAN:
else if (this.Connect("172.16.0.1:5000"))
{
_instanceName = "RIO-DataSource - WIFI";
_location = "172.16.0.1:5000";
return true;
}
// Nothing worked
return false;
}
else
{
return false;
}
}
private bool Connect(string instance)
{
if (!_backend.Connected)
{
// Try to connect
if (_backend.Connect(CreateIPEndPoint(instance)))
{
// Succeeded connecting
ConsoleLogger.WriteLine("RIODataSource", "Successfully connected to " + instance);
// Send the refresh Interval
int interval_ms = (int)(AppSettings.INTERVAL_SEC * 1000);
SetIntervalLength(interval_ms);
return true;
}
else
{
ConsoleLogger.WriteLine("RIODataSource", "Connecting failed to " + instance);
return false;
}
}
else
{
ConsoleLogger.WriteLine("RIODataSource", "Already connected!");
return false;
}
}
public void Disconnect()
{
if (_backend.Connected)
{
_backend.Disconnect();
}
}
public bool Connected
{
get { return _backend.Connected; }
}
// Handles IPv4 and IPv6 notation.
private IPEndPoint CreateIPEndPoint(string endPoint)
{
string[] ep = endPoint.Split(':');
if (ep.Length < 2) throw new FormatException("Invalid endpoint format");
IPAddress ip;
if (ep.Length > 2)
{
if (!IPAddress.TryParse(string.Join(":", ep, 0, ep.Length - 1), out ip))
{
throw new FormatException("Invalid ip-adress");
}
}
else
{
if (!IPAddress.TryParse(ep[0], out ip))
{
throw new FormatException("Invalid ip-adress");
}
}
int port;
if (!int.TryParse(ep[ep.Length - 1], NumberStyles.None, NumberFormatInfo.CurrentInfo, out port))
{
throw new FormatException("Invalid port");
}
return new IPEndPoint(ip, port);
}
#endregion
/// <summary>
/// Sets the time interval which is to be used by myRIO between UDP input channel packages.
/// </summary>
/// <param name="timeInterval">Interval in Milliseconds</param>
public void SetIntervalLength(int timeInterval)
{
_backend.SetInputChannelUpdateInterval(timeInterval);
}
}
}
using ModeliChart.Basics;
using ModeliChart.Log;
using System;
namespace ModeliChart.DataSource
{
public class RIODataSourceFactory: IDataSourceFactory
{
public IDataSource createDataSource(string instanceName, string location)
{
// On fail an Error will be thrown
try
{
return new RIODataSource(instanceName, location);
}
catch (Exception e)
{
ConsoleLogger.WriteLine("RIODataSourceFactory", "RIODataSource: " + e.Message);
return null;
}
}
}
}
using ModeliChart.Log;
using OxyPlot;
using System;
using System.Net;
using System.Net.Sockets;
namespace ModeliChart.DataSource
{
public class RIO_TCPBackend
{
// Connection
private enum MCCommand
{
SetInputChannelUpdateInterval, //Followed by an int specifying the interval length in milliseconds
StartSendingInputChannels, //Followed by a double specifying the absolute time to label from.
StopSendingInputChannels,
SetOutputChannel //Followed by a int32 for the channel const followed by a double value (Channel value volts)
}
private Socket tcpClient;
private UdpClient udpClient;
private bool listeningForUDP;
private bool connected;
// Data Storage: [Time, A0, A1,A2,...,C1] Length = 11
double[] _data;
// Initializes Variables
public RIO_TCPBackend()
{
_data = new double[11];
}
/// <summary>
/// The order of the array equals the enum: [A0, A1,A2,...,C1]
/// </summary>
/// <returns>Array of ISamples</returns>
public DataPoint[] getSamples()
{
if (_data != null)
{
// get the current time
double time = _data[0];
// create samples
DataPoint[] samples = new DataPoint[_data.Length - 1];
// Fill it with data
for (int i = 1; i < _data.Length; i++)
{
// Iterate through data and started with 1, shift -1 in samples
samples[i - 1] = new DataPoint(time, _data[i]);
}
return samples;
}
else
{
return new DataPoint[0];
}
}
/// <summary>
/// Returns the current connection state
/// </summary>
public bool Connected
{
get { return connected; }
}
public bool Connect(IPEndPoint ipEndpoint)
{
tcpClient = new Socket(SocketType.Stream, ProtocolType.Tcp);
// Connect using a timeout (1 second)
IAsyncResult result = tcpClient.BeginConnect(ipEndpoint, null, null);
bool success = result.AsyncWaitHandle.WaitOne(1000, true); // 1000 ms wait -> 1s timeout
if (!success)
{
// NOTE, MUST CLOSE THE SOCKET
tcpClient.Close();
// failed
return false;
}
else
{
tcpClient.NoDelay = true;
connected = true;
// Start listening for udp
startUDPListening();
listeningForUDP = true;
return true;
}
}
/// <summary>
/// Sets the time interval which is to be used by myRIO between UDP input channel packages.
/// </summary>
/// <param name="interval">Timer interval in Milliseconds</param>
public void SetInputChannelUpdateInterval(int interval)
{
byte[] data = new byte[sizeof(MCCommand) + sizeof(int)];
MCCommand command = MCCommand.SetInputChannelUpdateInterval;
BitConverter.GetBytes((int)command).CopyTo(data, 0);
BitConverter.GetBytes(interval).CopyTo(data, sizeof(MCCommand));
tcpClient.Send(data);
}
internal void Disconnect()
{
tcpClient.Disconnect(true);
connected = false;
this.listeningForUDP = false;
if (udpClient != null)
{
udpClient.Close();
}
ConsoleLogger.WriteLine("RioTcpController", "Disconnected.");
}
private void sendIfConnected(byte[] data)
{
if (tcpClient != null)
{
if (tcpClient.Connected)
{
try
{
tcpClient.Send(data);
}
catch (Exception)
{
}
}
}
}
internal void StartInputChannelUpdates(double startTime)
{
byte[] data = new byte[sizeof(MCCommand) + sizeof(double)];
MCCommand command = MCCommand.StartSendingInputChannels;
BitConverter.GetBytes((int)command).CopyTo(data, 0);
BitConverter.GetBytes(startTime).CopyTo(data, sizeof(MCCommand));
sendIfConnected(data);
}
internal void StopInputChannelUpdates()
{
byte[] data = new byte[sizeof(MCCommand)];
MCCommand command = MCCommand.StopSendingInputChannels;
BitConverter.GetBytes((int)command).CopyTo(data, 0);
sendIfConnected(data);
}
public void SetOutputChannelValue(Rio_Output rioOutput, double value)
{
byte[] data = new byte[sizeof(MCCommand) + sizeof(Rio_Output) + sizeof(double)];
MCCommand command = MCCommand.SetOutputChannel;
BitConverter.GetBytes((int)command).CopyTo(data, 0);
BitConverter.GetBytes((int)rioOutput).CopyTo(data, sizeof(MCCommand));
BitConverter.GetBytes(value).CopyTo(data, sizeof(MCCommand) + sizeof(Rio_Output));
sendIfConnected(data);
}
/// <summary>
/// Start receiving the data from the MyRIO
/// </summary>
public void startUDPListening()
{
udpClient = new UdpClient(6543);
if (udpClient != null)
{
IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, 6543);
// ConsoleLogger.WriteLine("Waiting for UDP broadcast to port " + 6543);
// Receive the Data, no state necessary for our needs
udpClient.BeginReceive(ReceiveCallback, null);
}
}
/// <summary>
/// Handles actually receiving the bytes and temporary storing it in the data
/// </summary>
/// <param name="asyncResult"></param>
public void ReceiveCallback(IAsyncResult asyncResult)
{
// Receive the actual data
IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, 6543);
byte[] buffer = udpClient.EndReceive(asyncResult, ref groupEP);
// Use BlockCopy to directly copy the bytes to the double-array, FAST!!!
Buffer.BlockCopy(buffer, 0, _data, 0, _data.Length);
// Push out the values;
NewValuesArrived(this, _data);
// Continue receiving if we still want to
if (listeningForUDP)
{
udpClient.BeginReceive(ReceiveCallback, null);
}
}
public event EventHandler<double[]> NewValuesArrived;
}
}
using System;
namespace ModeliChart.DataSource
{
#warning Turn all the length and sizes into Int32, because most method use int and won't accept uint
public static class NetworkProtocol
{
// Connection info
public const int TCP_PORT = 52062;
// In case something goes wrong it is necessary to be able to continue receiving so we use this magic number in front of every command both ways
public const UInt32 DEADBEEF = 0xDEADBEEF;
}
// Commands from the enum as int32 = 4Bytes
// Use Big-Endian (network order)
// Commands marked with require a a confirmation of success or fail
// If the command is followed by additional data, send/receive: int32 size/length -> byte[] data
// strings: int32 size -> UTF8_String char[size]
// arrays<type>: int32 length -> type[length] data
// data: int32 size -> byte[size] data
// length = number of elements followed -> read length*sizeof(type)
public enum Protocol_Server
{
Keep_Alive, // Send this command to check if the connection is still alive, do nothing in the backend
Play, // Start simulating
PlayFast, // Simulate as quick as possible -> double endTime_sec
Pause, // Pause simulation
Stop, // Stop and reset simulation
Instantiate, // Instantiate the FMUs -> string InstanceName
Model_DLL, // Receive the binary -> string InstanceName -> string GUID -> data file
ResourceFile, // Receive a Resource -> string InstanceName -> string FileName -> data file
RemoveModel, // Remove a model -> string InstanceName
Integer_ValueRefs, // Outputs from FMU -> string InstanceName -> uint32[length] valuRefs
Real_ValueRefs, // Outputs from FMU -> string InstanceName -> uint32[length] valuRefs
Boolean_ValueRefs, // Outputs from FMU -> string InstanceName -> uint32[length] valuRefs
Add_Channel_Link, // Add ChannelLink -> string masterInstanceName -> uint32 masterValueRef -> string slaveInstanceName -> uint32 slaveValueRef -> double factor -> double shift
Remove_Channel_Link,// Remove ChannelLink -> string masterInstanceName -> uint32 masterValueRef -> string slaveInstanceName -> uint32 slaveValueRef
Integer_SetValues, // Sets FMU Inputs -> string InstanceName -> uint32[length] valueRefs -> int32[length] values
Real_SetValues, // Sets FMU Inputs -> string InstanceName -> uint32[length] valueRefs -> double[length] values
Boolean_SetValues, // Sets FMU Inputs -> string InstanceName -> uint32[length] valueRefs -> int32[length] values (fmi2True = 1, fmi2False = 0)
};
// TCP Commands from backend to frontend
public enum Protocol_Client
{
// Send Error messages from the lowest levelso we do not need to take care of passing the error data to higher levels
// REMEMBER: Will always be called by the fmuLogger on fmi2Warning, fmi2Discard, fmi2Error or fmi2Fatal (for debugging may send another message with information)
Status, // Sends an fmi2Status & msg (optional 0 as size) -> int32 fmi2Status -> string Message
OperationSuccess, // Feedback if the requested operation succeeded or failed
OperationFail, // Feddback if the requested operation succeeded or failed
Values // Sends the values of the valueRefs -> string InstanceName -> double timestamp[sec] -> int32[length] intValues ->double[length] realValues -> int32[length]: boolValues
};
/* Type definitions */
enum fmi2Status
{
fmi2OK,
fmi2Warning,
fmi2Discard,
fmi2Error,
fmi2Fatal,
fmi2Pending
};
}
using FMUDllWrapper;
using ModeliChart.Basics;
using ModeliChart.Log;
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Threading.Tasks;
namespace ModeliChart.DataSource
{
public class TCPDataSource : DataSourceBase
{
// Store values and corresponding valueRefs
uint[] _intVr;
uint[] _realVr;
uint[] _boolVr;
ValuesCache<int> _intValues;
ValuesCache<double> _realValues;
ValuesCache<bool> _boolValues;
public TCPDataSource(string instanceName, string location) : base(instanceName, location)
{
StatusLogger.Progress = 0;
StatusLogger.Status = "Loading FMU file.";
// Try to find the fmu
if (!location.EndsWith("fmu"))
{
StatusLogger.Reset();
throw new ArgumentException("The given file is not a fmu.");
}
if (!File.Exists(location))
{
StatusLogger.Reset();
throw new FileNotFoundException("Fmu file does not exist.");
}
// Load the fmu
FMUInstance fmu = FMUInstance.fromFMU(location);
if (fmu == null)
{
StatusLogger.Reset();
throw new Exception("The FMU is corrupted.");
}
// Load the Channels
StatusLogger.Status = "Loading Channels from ModelDescription.";
StatusLogger.Progress = 20;
Channels = new ObservableCollection<IChannel>(FMUTools.createChannels(fmu, this));
// Send the model and resources
StatusLogger.Status = "Transfering Model to Backend.";
StatusLogger.Progress = 40;
// Check if we need to connect first
if (!TCPMaster.Connected)
{
TCPMaster.connect();
}
sendModelandRes(fmu);
// Instantiate
StatusLogger.Status = "Instantiating model.";
StatusLogger.Progress = 60;
TCPMaster.sendInstantiate(_instanceName);
// Set up variables
StatusLogger.Status = "Setting up variables.";
StatusLogger.Progress = 80;
setupVariables(fmu);
// Done
StatusLogger.Progress = 100;
ConsoleLogger.WriteLine("TCPDataSource " + _instanceName, "The DataSource has been created.");
StatusLogger.Reset();
}
private void TCPMaster_ValuesArrived(object sender, TcpValuesArgs e)
{
// Is it meant for me?
if (e.InstanceName.Equals(_instanceName))
{
// Store the data in the cache
_intValues.addPoint(e.TimeStamp, e.IntegerValues);
_realValues.addPoint(e.TimeStamp, e.RealValues);
_boolValues.addPoint(e.TimeStamp, e.BoolValues);
}
}
private void sendModelandRes(FMUInstance fmu)
{
// Unzip the fmu
string unzipDir = TempFiles.CreateTempFolder(_instanceName);
ZipFile.ExtractToDirectory(_location, unzipDir);
// Send Model DLL
string binaryPath = Path.Combine(new string[] { unzipDir, "binaries", "win32", fmu.ModelDescription.CoSimulation[0].modelIdentifier + ".dll" });
TCPMaster.sendModel(_instanceName, fmu.ModelDescription.guid, binaryPath);
// Send the resources
string resourceDir = Path.Combine(unzipDir, "resources");
if (Directory.Exists(resourceDir))
{
var resources = Directory.EnumerateFiles(resourceDir);
foreach (string resPath in resources)
{
string filename = Path.GetFileName(resPath);
TCPMaster.sendResource(_instanceName, resPath);
}
}
}
private void setupVariables(FMUInstance fmu)
{
// Assemble valueRefs
var vr = FMUTools.sortValueRefs(fmu);
_intVr = vr.Item1;
_realVr = vr.Item2;
_boolVr = vr.Item3;
// Create the cache for the data
_intValues = new ValuesCache<int>(_intVr.Length);
_realValues = new ValuesCache<double>(_realVr.Length);
_boolValues = new ValuesCache<bool>(_boolVr.Length);
// Send them
TCPMaster.sendIntegerValueRefs(_instanceName, _intVr);
TCPMaster.sendRealValueRefs(_instanceName, _realVr);
TCPMaster.sendBoolValueRefs(_instanceName, _boolVr);
}
public override void play()
{
// Register for receiving values
TCPMaster.ValuesArrived += TCPMaster_ValuesArrived;
TCPMaster.sendPlay();
}
public override void pause()
{
TCPMaster.sendPause();
}
public override void stopChild()
{
TCPMaster.sendStop();
// Unregister
TCPMaster.ValuesArrived -= TCPMaster_ValuesArrived;
_intValues.resetCache();
_realValues.resetCache();
_boolValues.resetCache();
}
// Push the values to the Channels
public override void updateValues()
{
lock (Channels)
{
#region int
// Receive the cache
var cache = _intValues.getCache();
// Assemble the samples to the channels
Parallel.For(0, cache.Length, i =>
{
// get Channels
uint valueRef = _intVr[i];
var vrChannels = _channelByValueRef[valueRef];
// Add the dataPoints
foreach (IChannel current in vrChannels)
{
current.Add(cache[i]);
}
});
#endregion
#region real
// Receive the cache
cache = _realValues.getCache();
// Assemble the samples to the channels
Parallel.For(0, cache.Length, i =>
{
// get Channels
uint valueRef = _realVr[i];
var vrChannels = _channelByValueRef[valueRef];
// Add the dataPoints
foreach (IChannel current in vrChannels)
{
current.Add(cache[i]);
}
});
#endregion
#region real
// Receive the cache
cache = _boolValues.getCache();
// Assemble the samples to the channels
Parallel.For(0, cache.Length, i =>
{
// get Channels
uint valueRef = _boolVr[i];
var vrChannels = _channelByValueRef[valueRef];
// Add the dataPoints
foreach (IChannel current in vrChannels)
{
current.Add(cache[i]);
}
});
#endregion
}
}
public override void doStep(double currentTime_sec)
{
// Well, we have no control here :)
}
public override void setValue(uint valueRef, double value, ChannelType type)
{
// Get the type of the Channel, when sharing the same vr, the type must be identical -> Just check the first one
var channels = _channelByValueRef[valueRef];
IChannel c = channels.First();
if (c != null && c.Settable && !double.IsNaN(value))
{
switch (c.ChannelType)
{
case ChannelType.Boolean:
TCPMaster.sendSetBoolValues(_instanceName, new uint[] { valueRef }, new bool[] { Convert.ToBoolean(value) });
break;
case ChannelType.Enum:
TCPMaster.sendSetIntegerValues(_instanceName, new uint[] { valueRef }, new int[] { Convert.ToInt32(value) });
break;
case ChannelType.Integer:
TCPMaster.sendSetIntegerValues(_instanceName, new uint[] { valueRef }, new int[] { (int)value });
break;
case ChannelType.Real:
TCPMaster.sendSetRealValues(_instanceName, new uint[] { valueRef }, new double[] { value });
break;
}
}
}
}
}
using ModeliChart.Basics;
using ModeliChart.Log;
using System;
namespace ModeliChart.DataSource
{
public class TCPDataSourceFactory : IDataSourceFactory
{
public IDataSource createDataSource(string instanceName, string location)
{
// On fail an Error will be thrown
try
{
return new TCPDataSource(instanceName, location);
}
catch (Exception e)
{
ConsoleLogger.WriteLine("TCPDataSourceFactory", "TCPDataSource: " + e.Message);
StatusLogger.Reset();
return null;
}
}
}
}
This diff is collapsed.
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="OxyPlot.Core" version="1.0.0" targetFramework="net452" />
</packages>
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading.Tasks;
using ModeliChart.Basics;
using ModeliChart.Log;
using FMUDllWrapper;
using ModeliChart.Settings;
using System.Collections.Concurrent;
namespace ModeliChart.DataSource
{
public enum FMUState
{
instantiated,
initializationMode,
slaveInitialized,
terminated,
undefined
}
public class FMUBackend
{
private FMUInstance _fmu;
private FMUState _state = FMUState.undefined;
private String _instanceName;
private String _location;
private System.Diagnostics.Stopwatch _sw; // For diagnostics only
// Collect the data here
private uint[] _realValueRefs;
private uint[] _intValueRefs;
private uint[] _boolValueRefs;
private double[] _realValues;
private int[] _intValues;
private bool[] _boolValues;
private List<IChannel> _channels; // Key: valueRef, Channel contains the information
// Initial
private double ABSOLUTTIME = 0;
long _DIAG_Total_US = 0;
long _DIAG_MinInterval_US;
long _DIAG_MaxInterval_US = 0;
int _DIAG_Iterations = 0;
const long _DIAG_ResetInterval_US = 5 * 1000 * 1000; // Reset the diagnose every 5s
const long TicksPerMicrosecond = 10;
// Hide the default constructor -> Only allow functional objects
private FMUBackend() { }
public FMUBackend(string instanceName, string location)
{
// Remember the name and location
_location = location;
_instanceName = instanceName;
// Initialize
initialize();
// Create channels
createChannels();
// Create StopWatch
_sw = new System.Diagnostics.Stopwatch();
}
#region public Getter & Setter
public string Name
{
get { return _instanceName; }
}
public string FMUPath
{
get { return _location; }
}
public List<IChannel> Channels
{
get
{
// ♥ Dictionary
return _channels;
}
}
/// <summary>
/// This is the order in which the data will be received
/// // real, int, bool
/// </summary>
public uint[] ValueRefs
{
get
{
var query = _realValueRefs.Concat(_intValueRefs).Concat(_boolValueRefs);
return query.ToArray();
}
}
#endregion
/// <summary>
/// Creates the FMUInstance, instantiates it and ends in initialization mode
/// </summary>
/// <param name="instanceName">Unique instance identifier</param>
/// <param name="location">Path to the .fmu file</param>
private void initialize()
{
// Check state
if (!(_state == FMUState.undefined || _state == FMUState.terminated))
{
// The fmu is already instantiated
return;
}
StatusLogger.Status = "Instantiating FMU";
// Check if the fmu file exists
if (!File.Exists(_location))
{
throw new FileNotFoundException("The given file location does not exist.");
}
if (!_location.EndsWith("fmu"))
{
throw new ArgumentException("The given file is not a fmu.");
}
// Create new Instance of FMU
_fmu = FMUInstance.fromFMU(_location);
StatusLogger.Status = "FMU instantiated";
// Create Logger Before loading so the Result can be logged
_fmu.LoggerMessageReceived += _fmu_LoggerMessageReceived;
// Load the files
DirectoryInfo path = new DirectoryInfo(_fmu.UnzipedPath);
Uri fmupath = new Uri(path.FullName);
ConsoleLogger.WriteLine("FMUBackend", "Start instantiation of FMI, Ver: " + _fmu.GetVersion());
// Instantiation and initialization
_fmu.Instantiate(_instanceName, fmupath.AbsoluteUri + "/resources", false, false);
// New State
_state = FMUState.instantiated;
_fmu.SetupExperiment(false, 0.0, 0.0, false, 10.0);
_fmu.EnterInitializationMode();
// New State
_state = FMUState.initializationMode;
StatusLogger.Status = "Initializing FMU (Create Channels)";
}
private void createChannels()
{
// load the valuerefs & create the arrays for the values
var vr = FMUTools.sortValueRefs(_fmu);
_intValueRefs = vr.Item1;
_intValues = new int[_intValueRefs.Length];
_realValueRefs = vr.Item2;
_realValues = new double[_realValueRefs.Length];
_boolValueRefs = vr.Item3;
_boolValues = new bool[_boolValueRefs.Length];
// Create the Channels
_channels = FMUTools.createChannels(_fmu);
}
/// <summary>
/// Releases all resources used by the FMU
/// </summary>
public void freeResources()
{
// Check if there is already an FMU loaded and dispose it
if (_fmu != null)
{
_fmu.freeResources();
// Reset Times
ABSOLUTTIME = 0;
}
}
#region Play, Pause, Stop
public void Play()
{
// Only play if _fmu is Connected
if (_fmu != null)
{
// Switching to play only possible from initMode
if (_state == FMUState.initializationMode)
{
_fmu.ExitInitializationMode();
_state = FMUState.slaveInitialized;
}
else
{
sendLog("Playing is only possible from initializationMode, currentMode: " + _state.ToString());
}
}
// Not connected yet
else
{
// Show hint in the console
sendLog("Please connect to the DataSource first.");
}
}
public void Pause()
{
// Stay in simulationMode: slaveInitialized
_sw.Stop();
}
/// <summary>
/// Stops the Simulation and resets the Simulation and Instruments
/// </summary>
public void Stop()
{
_state = FMUState.undefined;
_sw.Stop();
// Reset the Simulation Time
ABSOLUTTIME = 0;
// Reset the Simulation
if (_fmu != null)
{
#warning This should be used: _fmu.Reset();
#region dirty fix for fmiReset not working in some FMus
// Clear the fmu
freeResources();
_state = FMUState.undefined;
// Create new instance
initialize();
#endregion
}
}
#endregion
#region Simulation
public double[] simulateValues(double currentTime_sec)
{
#region Diagnostics: Get avg. times for simulation
if (_sw.Elapsed.Ticks > 0)
{
// Convert to us, 1000 us/ms
long ticks = _sw.Elapsed.Ticks;
long us = ticks / TicksPerMicrosecond;
_DIAG_Total_US += us;
if (us < _DIAG_MinInterval_US)
{
_DIAG_MinInterval_US = us;
}
if (us > _DIAG_MaxInterval_US)
{
_DIAG_MaxInterval_US = us;
}
}
_sw.Stop();
_sw.Reset();
//Diagnostics
_sw.Start();
_DIAG_Iterations++;
// Show the diagnose and reset & restart the diagnose
if (_DIAG_Total_US > _DIAG_ResetInterval_US)
{
sendLog("Avg. interval: " + (((double)_DIAG_Total_US / (double)_DIAG_Iterations) * 1e-3).ToString() + "ms (Max: " + (double)_DIAG_MaxInterval_US * 1e-3 + "ms, Min: " + (double)_DIAG_MinInterval_US * 1e-3 + "ms)");
_DIAG_MinInterval_US = long.MaxValue;
_DIAG_MaxInterval_US = 0;
_DIAG_Total_US = 0;
_DIAG_Iterations = 0;
}
#endregion Diagnostics
// Calculate the interval to get to the currentTime_sec
double interval_sec = currentTime_sec - ABSOLUTTIME;
// only simulate if we are in the correct state
if (_state == FMUState.slaveInitialized)
{
// Compute the Simulation from absTime -> (absTime+Interval) = newAbsTime
_fmu.DoStep(ABSOLUTTIME, interval_sec, false);
// New CurrentPoint is currentTime_sec
ABSOLUTTIME = currentTime_sec;
// ABSOLUTTIME = Math.Round(ABSOLUTTIME, 5);
// Retrieve the Data and write them into arrays, this is quick!!!
// Do not assemble into a dictionary, only when the data gets pulled: getLatestSamples()
_realValues = _fmu.GetReal(_realValueRefs);
_intValues = _fmu.GetInteger(_intValueRefs);
_boolValues = _fmu.GetBoolean(_boolValueRefs);
// LINQ to create res vector
var query = _realValues.Concat(_intValues.Select(i => Convert.ToDouble(i)))
.Concat(_boolValues.Select(b => Convert.ToDouble(b)));
return query.ToArray();
}
else
{
return null;
}
}
#endregion
/// <summary>
/// Sets the value in the FMUInstance
/// It is expected that the modelVariable for valueRef is settable
/// </summary>
/// <param name="valueRef">ValueReference from ModelDescription</param>
/// <param name="value">Value that will be set</param>
public void SetIntValue(uint valueRef, int value)
{
// In these two states setting variables is allowed
if (_state == FMUState.initializationMode || _state == FMUState.slaveInitialized)
{
_fmu.SetInteger(new uint[] { valueRef }, new int[] { value });
}
}
/// <summary>
/// Sets the value in the FMUInstance
/// </summary>
/// <param name="valueRef">ValueReference from ModelDescription</param>
/// <param name="value">Value that will be set</param>
public void SetRealValue(uint valueRef, double value)
{
// In these two states setting variables is allowed
if (_state == FMUState.initializationMode || _state == FMUState.slaveInitialized)
{
_fmu.SetReal(new uint[] { valueRef }, new double[] { value });
}
}
/// <summary>
/// Sets the value in the FMUInstance
/// </summary>
/// <param name="valueRef">ValueReference from ModelDescription</param>
/// <param name="value">Value that will be set</param>
public void SetBoolValue(uint valueRef, bool value)
{
// In these two states setting variables is allowed
if (_state == FMUState.initializationMode || _state == FMUState.slaveInitialized)
{
_fmu.SetBoolean(new uint[] { valueRef }, new bool[] { value });
}
}
/// <summary>
/// Prints the message to the console
/// </summary>
/// <param name="message">The Message to be sent</param>
void _fmu_LoggerMessageReceived(string message)
{
//ignore this message:
//"CVODE: CVode failed with NONE:\n Internal t = 8 and h = 5.32907e-016 are such that t + h = t on the next step. The solver will continue anyway."
if (!message.EndsWith("The solver will continue anyway."))
{
sendLog("FMULog: " + message);
}
}
private void sendLog(string msg)
{
ConsoleLogger.WriteLine("FMUBackend", msg);
}
}
}
using ModeliChart.Basics;
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
namespace ModeliChart.DataSource
{
public class FMUDataSource : DataSourceBase
{
// The backend will do the simulation, the FMUDataSource only serves the IDataSource interface
private FMUBackend _backend;
private ValuesCache<double> _cache;
// know which order the data has
private uint[] _valueRefs;
public FMUDataSource(string instanceName, string location) : base(instanceName, location)
{
// Setup the backend
_backend = new FMUBackend(instanceName, location);
// Setup the channels for the dataSOurce
setupChannels();
}
~FMUDataSource()
{
// Make sure to clean up unmanaged code
_backend.freeResources();
}
private void setupChannels()
{
// Take over the values from the backend
_valueRefs = _backend.ValueRefs;
// Create cache
_cache = new ValuesCache<double>(_valueRefs.Length);
// setupChannels for this dataSource
foreach (var current in _backend.Channels)
{
current.DataSource = this;
}
// Assemble the frontEnd Collections
Channels = new ObservableCollection<IChannel>(_backend.Channels);
}
/// <summary>
/// The DataSource updates the samples and channels to the current time.
/// The backend must work in realtime.
/// </summary>
public override void updateValues()
{
lock (Channels)
{
// Receive the cache
var cache = _cache.getCache();
// Assemble the samples to the channels
Parallel.For(0, cache.Length, i =>
{
// get Channels
uint valueRef = _valueRefs[i];
var vrChannels = _channelByValueRef[valueRef];
// Add the dataPoints
foreach (IChannel current in vrChannels)
{
current.Add(cache[i]);
}
});
}
}
// Format of e: [time, _realValues..., _intValues]
public override void doStep(double currentTime_sec)
{
// Simulate to the currentTime
double[] res = _backend.simulateValues(currentTime_sec);
if (res != null)
{
// Copy the values to the cache
_cache.addPoint(currentTime_sec, res);
}
}
public override void setValue(uint valueRef, double value, ChannelType type)
{
switch (type)
{
case ChannelType.Integer:
_backend.SetIntValue(valueRef, (int)value);
break;
case ChannelType.Enum:
_backend.SetIntValue(valueRef, (int)value);
break;
case ChannelType.Real:
_backend.SetRealValue(valueRef, value);
break;
case ChannelType.Boolean:
_backend.SetBoolValue(valueRef, Convert.ToBoolean(value));
break;
}
}
// Be able to controll the simulation
public override void play()
{
_backend.Play();
}
public override void pause()
{
_backend.Pause();
}
public override void stop()
{
_backend.Stop();
_cache.resetCache();
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment