File Format Example |
Below, an example implementation of a RelaxISPlugin_FileFormat is shown.
The function of the plugin is chosen for illustrative purposes.
Reference to the RelaxIS_SDK.dll
.NET Framework 4.7.2 target (class library)
Recommended: Development environment with compiler e.g. RelaxIS SDK Code Editor, Microsoft Visual Studio
This examples illustrates the implementation of a file format plugin.
It identifies the file by checking if the first line in the file is 'MyFileFormat File Format'.
It then reads a numeric value from the next line and interprets it as a temperature value valid for all spectra.
The plugin then reads data from 5 tab-separated columns:
Spectrum Index
Frequencz
Z'
Z''
DC Voltage
The plugin demonstrates two ways of separating the read data into spectra, manually by checking the Spectrum Index value, and automatically by adding an EISRawData object with datapoints that define the Spectrum Index as a SplitValue.
// <copyright file="MyFileFormat.cs" company="rhd instruments GmbH and Co. KG"> // Copyright (c) rhd instruments GmbH and Co. KG. All rights reserved. // Licensed under the MIT No Attribution (MIT-0) license. See section 'License' in the 'SDK Examples / Tutorials' topic for full license information. // </copyright> namespace RelaxIS_SDK_Examples.Plugins { using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using RelaxIS_SDK.Common; using RelaxIS_SDK.Common.FileFormat; using RelaxIS_SDK.Plugins; /*** * FileFormat plugins implement a way for RelaxIS to read data from an arbitrary file without user interaction. * To this end, the plugin implements two functions: Identification of the input data to determine if the plugin * will be able to read data from it and the function to read the data. * When trying to read input data, RelaxIS will call all available FileFormat plugins in turn until one can actually * read spectra from the input data. */ /// <summary> /// Implements an example <see cref="RelaxISPlugin_FileFormat"/> that reads data from a custom file format automatically. /// </summary> /// <remarks>It shows two different ways of splitting the data.</remarks> internal class MyFileFormat : RelaxISPlugin_FileFormat { /*** * First, implement the default plugin properties Name and Description that describe the plugin. * This is mainly used for display purposes, for example in the plugin list in the RelaxIS settings dialog. */ /// <inheritdoc/> public override string Name { get { return "MyFileFormat"; } } /// <inheritdoc/> public override string Description { get { return "An example file format that looks for the string 'MyFileFormat File Format' in the first line of a file and then reads data from 5 columns below."; } } /// <summary> /// Gets or sets a value indicating whether data should be split automatically by RelaxIS or is split manually in the plugin. /// </summary> /// <remarks>This property is just for illustrating different plugin code in this example.</remarks> public bool LetRelaxISSplit { get; set; } /*** * IdentifyFile should check the contents of the input data to determine if it is the correct format for this plugin. * The way to do this varys depending on the format, here we just check the first line as an identifier. * It is possible to use the SourcePath property to get the path to the original file. However, this path is not always * available. E.g. the data could be coming from the Windows clipboard, or via the RelaxIS WCF link. Therefore it is usually * better to not rely on the filename for file identification. */ /// <inheritdoc/> public override bool IdentifyFile(Stream input) { using (var sr = new StreamReader(input)) { var l = sr.ReadLine(); if (string.IsNullOrEmpty(l)) { return false; } return l.Trim() == "MyFileFormat File Format"; } } /*** * The GetData function should read the file and return the result. * Here, this is illustrated in two ways, once by manually splitting the data into multiple spectra, depending on * the Spectrum Index column value and once by adding the extracted data into a EISRawData object and letting * RelaxIS do the splitting automatically. * The first line after the header in this fictional file format contains the current temperature */ /// <inheritdoc/> public override FileFormatResult GetData(Stream input) { // Depending on the property value, data is either split automatically or manually. // This is just for illustration. If possible, it is best to manually split the data directly. if (this.LetRelaxISSplit) { return SplitAutomatically(input); } else { return SplitManually(input); } } private static FileFormatResult SplitManually(Stream input) { using (var sr = new StreamReader(input)) { // Read header line to skip it var l = sr.ReadLine(); // Read the temperature line l = sr.ReadLine(); var temperatureValue = ToDouble(l); // Store this as a metadata object for adding it to the result spectra. var temperature = new Metadata(MetadataNames.TEMPERATURE, temperatureValue); // Read the first data line l = sr.ReadLine(); // Set up the data objects that will store the extracted data int lastIdx = 0; var res = new FileFormatResult(); var currentSpectrum = new ImpedanceSpectrum(); var dcValues = new List<double>(); currentSpectrum.Metadata.Add(temperature); while (!string.IsNullOrEmpty(l)) { try { // Read a line of 5 numeric values, separated by a tabstop var parts = l.Split('\t'); var numeric = parts.Select(p => ToDouble(p)).Where(d => !double.IsNaN(d)); if (numeric.Count() != 5) { continue; } var values = numeric.ToList(); // Values are Spectrum Index | Frequency | Z' | Z'' | DC Voltage var idx = (int)values[0]; var freq = values[1]; var zr = values[2]; var zi = values[3]; var dc = values[4]; dcValues.Add(dc); // Split into spectra by Index if (lastIdx != idx) { if (currentSpectrum != null && currentSpectrum.Data.Count > 0) { // Add the collected DC voltage values as metadata. currentSpectrum.Metadata.Add(MetadataNames.DCVOLTAGE, dcValues.Average()); res.Spectra.Add(currentSpectrum); } currentSpectrum = new ImpedanceSpectrum(); dcValues.Clear(); currentSpectrum.Metadata.Add(temperature); lastIdx = idx; } currentSpectrum.Data.Add(new EISDatapoint(freq, zr, zi)); } finally { l = sr.ReadLine(); } } // Add the current spectrum again, because it would be missed otherwise if (currentSpectrum != null && currentSpectrum.Data.Count > 0) { // Add the collected DC voltage values as metadata. currentSpectrum.Metadata.Add(MetadataNames.DCVOLTAGE, dcValues.Average()); res.Spectra.Add(currentSpectrum); } return res; } } private static FileFormatResult SplitAutomatically(Stream input) { using (var sr = new StreamReader(input)) { // Read header line var l = sr.ReadLine(); // Read the temperature line l = sr.ReadLine(); var temperatureValue = ToDouble(l); // Store this as a metadata object for adding it to the result spectra. var temperature = new Metadata(MetadataNames.TEMPERATURE, temperatureValue); // Read the first data line l = sr.ReadLine(); // Lets RelaxIS split the data automatically based on the Index column value. // Alternatively, use SplitMode.Automatic, to make RelaxIS analyze the frequencies of the datapoints and // split when the frequency direction changes. // In that case, it is unneccessary to assign the index value as a SplitValue for the EISMetaDatapoint objects. var res = new FileFormatResult(); var rawData = new EISRawData() { SplitMode = SplitMode.BySplitValues, }; res.RawData.Add(rawData); while (!string.IsNullOrEmpty(l)) { try { // Read a line of 5 numeric values, separated by a tabstop var parts = l.Split('\t'); var numeric = parts.Select(p => ToDouble(p)).Where(d => !double.IsNaN(d)); if (numeric.Count() != 5) { continue; } var values = numeric.ToList(); // Values are Spectrum Index | Frequency | Z' | Z'' | DC Voltage var idx = (int)values[0]; var freq = values[1]; var zr = values[2]; var zi = values[3]; var dc = values[4]; var dcValue = new Metadata(MetadataNames.DCVOLTAGE, dc); var collection = new MetadataCollection() { temperature, dcValue }; // The idx field is added as the 'SplitValue' property to the datapoint. // As selected above by SplitMode = SplitMode.BySplitValues, this is used by RelaxIS to split the data. // Alternatively, you could split by number of datapoints, or automatically by frequency values. rawData.Datapoints.Add(new EISMetaDatapoint(new EISDatapoint(freq, zr, zi), collection, idx)); } finally { l = sr.ReadLine(); } } return res; } } private static double ToDouble(string p) { double res; return double.TryParse(p, System.Globalization.NumberStyles.Float, CultureInfo.InvariantCulture, out res) ? res : double.NaN; } } }