using System; using System.IO; using System.Runtime.InteropServices; using System.Collections.Generic; using System.Linq; using DHI.Generic.MikeZero; using DHI.Generic.MikeZero.DFS; namespace org.openda.dotnet.DHIStochObserver { public enum TimeInterval { Second = 1400, Minute, Hour, Day, Month, Year, } public enum MathType { Sum, Average, Min, Max } /// /// Abstract class that handles all direct access to .dfs-files. Uses static methods from DFSWrapper in /// DHI.Generic.MikeZero.DFS.dll as well as direct calls into the ufs.dll /// public class DFSBase : IDisposable { #region Calls directly into ufs.dll because the wrapped call does not work on vista due to something with strings. const string UFSDll = "ufs.dll"; // Name of dll. Should be in path /// /// Call directly into ufs.dll because the wrapped call does not work on vista due to something with strings. /// /// /// /// /// /// /// [DllImport(UFSDll, CharSet = CharSet.None, CallingConvention = CallingConvention.StdCall)] internal extern static int dfsGetGeoInfoUTMProj(IntPtr HeaderPointer, ref IntPtr Projection, ref double longitude, ref double Latitude, ref double Orientation); #endregion //Keeps track of the data in the buffer private int _currentTimeStep = 0; private int _currentItem = 1; protected float[] dfsdata; //Buffer used to fill data into private double[] _times; //this array contains the timesteps from non equidistant calendar axis in file units. Used only for writing private double? _deleteValue; protected IntPtr _filePointer = IntPtr.Zero; protected IntPtr _headerPointer = IntPtr.Zero; protected bool _initializedForWriting = false; private TimeInterval timeStepUnit = TimeInterval.Second; protected TimeSpan _timeStep = TimeSpan.Zero; public TimeAxisType _timeAxis { get; set; } protected SpaceAxisType _spaceAxis; protected string AbsoluteFileName; private string _filename; protected int _numberOfLayers = 1; protected int _numberOfColumns = 1; protected int _numberOfRows = 1; protected double _xOrigin = 0; protected double _yOrigin = 0; protected double _orientation = 0; protected double _gridSize = 1; protected List _timesteps = new List(); private int _status; private int NumberOfTimeStepsWritten = 0; #region Constructors public DFSBase() { } /// /// Creates a new .dfs file /// /// /// /// public DFSBase(string DFSFileName, int NumberOfItems) : this() { _filename = DFSFileName; AbsoluteFileName = Path.GetFullPath(DFSFileName); this.NumberOfItems = NumberOfItems; _initializedForWriting = true; } protected void CopyItemInfo(DFSBase TemplateDFS) { for (int i = 0; i < TemplateDFS.Items.Count(); i++) { Items[i].Name = TemplateDFS.Items[i].Name; Items[i].EumItem = TemplateDFS.Items[i].EumItem; Items[i].EumUnit = TemplateDFS.Items[i].EumUnit; Items[i].ValueType = TemplateDFS.Items[i].ValueType; } } /// /// Opens an existing dfs-file /// /// public DFSBase(string DFSFileName) : this() { _filename = DFSFileName; AbsoluteFileName = Path.GetFullPath(DFSFileName); try { DfsDLLWrapper.dfsFileRead(AbsoluteFileName, out _headerPointer, out _filePointer); } catch (Exception e) { return; //Not a valid file. } NumberOfItems = DfsDLLWrapper.dfsGetNoOfItems(_headerPointer); string eum_unit = ""; int unit = 0; int data_type = 0; int item_type = 0; float x = 0; float y = 0; float z = 0; float dx = 0; float dy = 0; float dz = 0; IntPtr name = new IntPtr(); //Reads the projection LastStatus = dfsGetGeoInfoUTMProj(_headerPointer, ref name, ref _xOrigin, ref _yOrigin, ref _orientation); //Reads the space axis _spaceAxis = (SpaceAxisType)DfsDLLWrapper.dfsGetItemAxisType(FirstItem.ItemPointer); //Now read axes info dependent on the type of axis switch (_spaceAxis) { case SpaceAxisType.CurveLinearD2: break; case SpaceAxisType.CurveLinearD3: break; case SpaceAxisType.EqD0: break; case SpaceAxisType.EqD1: break; case SpaceAxisType.EqD2: //DFS2 from MikeShe DfsDLLWrapper.dfsGetItemAxisEqD2(FirstItem.ItemPointer, out item_type, out eum_unit, out _numberOfColumns, out _numberOfRows, out x, out y, out dx, out dy); break; case SpaceAxisType.EqD3: //DFS3 from MikeShe DfsDLLWrapper.dfsGetItemAxisEqD3(FirstItem.ItemPointer, out item_type, out eum_unit, out _numberOfColumns, out _numberOfRows, out _numberOfLayers, out x, out y, out z, out dx, out dy, out dz); break; case SpaceAxisType.NeqD1: var coords = new Coords[1]; DfsDLLWrapper.dfsGetItemAxisNeqD1(FirstItem.ItemPointer, out unit, out eum_unit, out data_type, out coords); break; case SpaceAxisType.NeqD2: break; case SpaceAxisType.NeqD3: break; case SpaceAxisType.Undefined: break; default: break; } _gridSize = dx; //Prepares an array of floats to recieve the data dfsdata = new float[_numberOfColumns * _numberOfRows * _numberOfLayers]; //Now look at time axis _timeAxis = (TimeAxisType)DfsDLLWrapper.dfsGetTimeAxisType(_headerPointer); string startdate = ""; string starttime = ""; double tstart = 0; double tstep = 0; int nt = 0; int tindex = 0; switch (_timeAxis) { case TimeAxisType.TimeEquidistant: //Some DFS2 here DfsDLLWrapper.dfsGetEqTimeAxis(_headerPointer, out unit, out eum_unit, out tstart, out tstep, out nt, out tindex); break; case TimeAxisType.CalendarEquidistant: //Dfs2 and dfs3 here DfsDLLWrapper.dfsGetEqCalendarAxis(_headerPointer, out startdate, out starttime, out unit, out eum_unit, out tstart, out tstep, out nt, out tindex); if (unit == 1400) _timeStep = TimeSpan.FromSeconds(tstep); else if (unit == 1401) //This is a guess _timeStep = TimeSpan.FromMinutes(tstep); else if (unit == 1402) _timeStep = TimeSpan.FromHours(tstep); break; case TimeAxisType.TimeNonEquidistant: //This has not been tested DfsDLLWrapper.dfsGetNeqTimeAxis(_headerPointer, out unit, out eum_unit, out tstart, out tstep, out nt, out tindex); break; case TimeAxisType.CalendarNonEquidistant://Only dfs0 can have varying time steps DfsDLLWrapper.dfsGetNeqCalendarAxis(_headerPointer, out startdate, out starttime, out unit, out eum_unit, out tstart, out tstep, out nt, out tindex); break; case TimeAxisType.Undefined: break; default: break; } NumberOfTimeStepsWritten = nt; timeStepUnit = (TimeInterval)unit; if (_timeAxis == TimeAxisType.CalendarNonEquidistant | _timeAxis == TimeAxisType.TimeEquidistant) _times = new double[nt]; if (startdate != "" & starttime != "") { _timesteps.Add(DateTime.Parse(startdate).Add(TimeSpan.Parse(starttime))); } else //Time equidistant files enter here. _timesteps.Add(new DateTime(2002, 1, 1)); //Now build the list of timesteps for (int i = 1; i < nt; i++) { if (_timeAxis == TimeAxisType.CalendarNonEquidistant) //dfs0 with time varying. _timesteps.Add(_timesteps[0].Add(GetTimeSpan(i))); else _timesteps.Add(_timesteps[i - 1].Add(_timeStep)); } } #endregion public virtual void CopyFromTemplate(DFSBase dfs) { _timeAxis = dfs._timeAxis; if (dfs._timeAxis == TimeAxisType.CalendarEquidistant || dfs._timeAxis == TimeAxisType.TimeEquidistant) { this.TimeOfFirstTimestep = dfs.TimeOfFirstTimestep; this.TimeStep = dfs.TimeStep; } this.DeleteValue = dfs.DeleteValue; if (DfsDLLWrapper.dfsIsFileCompressed(dfs._headerPointer)) { var en = DfsDLLWrapper.dfsGetEncodeKeySize(dfs._headerPointer); int[] xkey = new int[en]; int[] ykey = new int[en]; int[] zkey = new int[en]; DfsDLLWrapper.dfsGetEncodeKey(dfs._headerPointer, xkey, ykey, zkey); DfsDLLWrapper.dfsSetEncodeKey(_headerPointer, xkey, ykey, zkey, en); } } #region Read methods //Gets the timespan for a time step using readitemtimestep. Should only be used with CalendarNonEquidistant protected TimeSpan GetTimeSpan(int TimeStep) { TimeSpan ts = TimeSpan.Zero; double time = 0; while (_currentTimeStep < TimeStep) { IncrementItemTimeStep(); DfsDLLWrapper.dfsSkipItem(_headerPointer, _filePointer); } DfsDLLWrapper.dfsReadItemTimeStep(_headerPointer, _filePointer, out time, dfsdata); IncrementItemTimeStep(); switch (this.timeStepUnit) { case TimeInterval.Second: ts = TimeSpan.FromSeconds(time); break; case TimeInterval.Minute: ts = TimeSpan.FromMinutes(time); break; case TimeInterval.Hour: ts = TimeSpan.FromHours(time); break; case TimeInterval.Day: break; case TimeInterval.Month: break; case TimeInterval.Year: break; default: break; } return ts; } /// /// Returns the zero-based index of the TimeStep closest to the TimeStamp. If the timestamp falls exactly between two timestep the smallest is returned. /// If the TimeStamp is before the first timestep 0 is returned. /// If the TimeStamp is after the last timestep the index of the last timestep is returned /// /// /// public int GetTimeStep(DateTime TimeStamp) { if (TimeStamp < TimeSteps.First() || NumberOfTimeSteps == 1) return 0; int TimeStep; //fixed timestep if (_timeAxis == TimeAxisType.CalendarEquidistant) TimeStep = (int)Math.Round(TimeStamp.Subtract(TimeSteps.First()).TotalSeconds / _timeStep.TotalSeconds, 0); //Variabale timestep else { //Last timestep is known if (TimeStamp >= TimeSteps[NumberOfTimeSteps - 1]) return NumberOfTimeSteps - 1; int i = 1; //Loop the timesteps while (TimeStamp > TimeSteps[i]) { i++; } //Check if last one was actually closer if (TimeSteps[i].Subtract(TimeStamp) < TimeStamp.Subtract(TimeSteps[i - 1])) return i; else return i - 1; } return Math.Min(NumberOfTimeSteps, TimeStep); } /// /// Moves to the timestep and item /// Returns true if it was actually necessary to move /// Note that it is not possible to move backwards into something that has been written without /// /// /// /// private bool MoveToItemTimeStep(int TimeStep, int Item) { TimeStep = Math.Min(TimeStep, NumberOfTimeStepsWritten); Item = Math.Min(Item, NumberOfItems); if (TimeStep != _currentTimeStep || Item != _currentItem) { _currentTimeStep = TimeStep; _currentItem = Item; if (TimeStep == NumberOfTimeStepsWritten) { DfsDLLWrapper.dfsFindItemDynamic(_headerPointer, _filePointer, TimeStep - 1, NumberOfItems); //Spools to last item DfsDLLWrapper.dfsSkipItem(_headerPointer, _filePointer); // now at end _currentItem = 1; return true; } else { //Spools to the correct Item and TimeStep DfsDLLWrapper.dfsFindItemDynamic(_headerPointer, _filePointer, TimeStep, Item); return true; } } return false; } private bool EndOfFile { get { return _currentTimeStep == NumberOfTimeStepsWritten; } } private void IncrementItemTimeStep() { _currentItem++; if (_currentItem > NumberOfItems) { _currentItem = 1; _currentTimeStep++; } } /// /// Reads data for the TimeStep and Item and fills them into the buffer. /// Time steps counts from 0 and Item from 1. /// /// /// public float[] ReadItemTimeStep(int TimeStep, int Item) { double time = 0; MoveToItemTimeStep(TimeStep, Item); DfsDLLWrapper.dfsReadItemTimeStep(_headerPointer, _filePointer, out time, dfsdata); IncrementItemTimeStep(); return dfsdata; } #endregion #region Math methods /// /// Multiplies a factor to the values in the item and timestep /// /// /// /// public void MultiplyItemTimeStep(int TimeStep, int Item, double factor) { ReadItemTimeStep(TimeStep, Item); for (int i = 0; i < dfsdata.Count(); i++) if (dfsdata[i] != (float)DeleteValue) dfsdata[i] *= (float)factor; WriteItemTimeStep(TimeStep, Item, dfsdata); } /// /// Adds a factor to the values in the item and timestep /// /// /// /// public void AddToItemTimeStep(int TimeStep, int Item, double factor) { ReadItemTimeStep(TimeStep, Item); for (int i = 0; i < dfsdata.Count(); i++) if (dfsdata[i] != (float)DeleteValue) dfsdata[i] += (float)factor; WriteItemTimeStep(TimeStep, Item, dfsdata); } /// /// Sums the values of the items to the selected time interval and puts them in the new dfs file /// Assumes that the there are delete values at the same places in all items and timesteps! /// /// /// /// public void TimeAggregation(int[] Items, DFSBase df, TimeInterval SumTim, int Tsteps, MathType mathtype) { Dictionary BufferData = new Dictionary(); List NonDeleteIndex = null; List DeleteIndex = null; //Initialize all items foreach (var j in Items) BufferData[j] = new float[dfsdata.Count()]; DateTime LastPrint = TimeSteps[0]; bool PrintNow = false; int tstepCounter = 0; //Loop the time steps for (int i = 0; i < NumberOfTimeSteps; i++) { tstepCounter++; switch (SumTim) { case TimeInterval.Year: PrintNow = (LastPrint.Year + Tsteps == TimeSteps[i].Year); break; case TimeInterval.Month: int nextmonth = LastPrint.Month + Tsteps; if (nextmonth > 12) nextmonth -= 12; PrintNow = (nextmonth == TimeSteps[i].Month); break; case TimeInterval.Day: PrintNow = ((TimeSteps[i].Subtract(LastPrint) >= TimeSpan.FromDays(Tsteps))); break; default: break; } //Now print summed values and empty buffer if (PrintNow) { foreach (var j in Items) { if (mathtype == MathType.Average) //Average, and a division with the number of time steps is required foreach (var n in NonDeleteIndex) BufferData[j][n] = BufferData[j][n] / ((float)tstepCounter); df.WriteItemTimeStep(df.NumberOfTimeSteps, j, BufferData[j]); foreach (var n in NonDeleteIndex) if (mathtype == MathType.Min) BufferData[j][n] = float.MaxValue; else if (mathtype == MathType.Max) BufferData[j][n] = float.MinValue; else BufferData[j][n] = 0; } tstepCounter = 0; LastPrint = TimeSteps[i]; PrintNow = false; } //Sum all items foreach (var j in Items) { ReadItemTimeStep(i, j); if (DeleteIndex == null) //For the first time step build a list of the non-delete values { DeleteIndex = new List(); NonDeleteIndex = new List(); for (int k = 0; k < dfsdata.Count(); k++) { if (dfsdata[k] == DeleteValue) { DeleteIndex.Add(k); foreach (var n in Items) BufferData[n][k] = dfsdata[k]; //Set the delete values for all items } else NonDeleteIndex.Add(k); foreach (var n in Items) { if (mathtype == MathType.Min) BufferData[n][k] = float.MaxValue; else if (mathtype == MathType.Max) BufferData[n][k] = float.MinValue; else BufferData[n][k] = 0; } } } var arr = BufferData[j]; foreach (int k in NonDeleteIndex) if (mathtype == MathType.Min) arr[k] = Math.Min(arr[k], dfsdata[k]); else if (mathtype == MathType.Max) arr[k] = Math.Max(arr[k], dfsdata[k]); else arr[k] += dfsdata[k]; } } //print the last summed values foreach (var j in Items) { if (mathtype == MathType.Average) //If not sum it is average and a division with the number of time steps is required foreach (var n in NonDeleteIndex) BufferData[j][n] = BufferData[j][n] / ((float)tstepCounter); df.WriteItemTimeStep(df.NumberOfTimeSteps, j, BufferData[j]); } } #endregion #region Write methods /// /// Writes data for the TimeStep and Item /// /// /// public void WriteItemTimeStep(int TimeStep, int Item, float[] data) { if (!_initializedForWriting) InitializeForWriting(); if (_filePointer == IntPtr.Zero) CreateFile(); MoveToItemTimeStep(TimeStep, Item); double time = 0; if (_timeAxis == TimeAxisType.CalendarNonEquidistant & _currentTimeStep > 0) { TimeSpan ts = TimeSteps[_currentTimeStep].Subtract(TimeSteps[0]); switch (timeStepUnit) { case TimeInterval.Second: time = ts.TotalSeconds; break; case TimeInterval.Minute: time = ts.TotalMinutes; break; case TimeInterval.Hour: time = ts.TotalHours; break; default: break; } } if (EndOfFile) { if (_currentItem == NumberOfItems) NumberOfTimeStepsWritten++; AppendTimeStep(TimeSteps.Last().Add(_timeStep)); } //Writes the data DfsDLLWrapper.dfsWriteItemTimeStep(_headerPointer, _filePointer, time, data); IncrementItemTimeStep(); } protected virtual void AppendTimeStep(DateTime Time) { if (_timeAxis != TimeAxisType.CalendarNonEquidistant & _currentTimeStep > 0) { TimeSteps.Add(Time); } } /// /// Opens the file for writing. First closes the file since it has already been opened for reading /// protected void InitializeForWriting() { Dispose(false); DfsDLLWrapper.dfsFileEdit(_filename, out _headerPointer, out _filePointer); _initializedForWriting = true; for (int i = 0; i < NumberOfItems; i++) { var ip = DfsDLLWrapper.dfsItemD(_headerPointer, i + 1); Items[i].ItemPointer = ip; } } private void CreateFile() { WriteGeoInfo(); WriteTime(); foreach (Item I in Items) { WriteItemInfo(I); if (_spaceAxis == SpaceAxisType.EqD2) DfsDLLWrapper.dfsSetItemAxisEqD2(I.ItemPointer, 1000, _numberOfColumns, _numberOfRows, 0, 0, (float)_gridSize, (float)_gridSize); else if (_spaceAxis == SpaceAxisType.EqD3) DfsDLLWrapper.dfsSetItemAxisEqD3(I.ItemPointer, 1000, _numberOfColumns, _numberOfRows, _numberOfLayers, 0, 0, 0, (float)_gridSize, (float)_gridSize, (float)_gridSize); else if (_spaceAxis == SpaceAxisType.EqD0) DfsDLLWrapper.dfsSetItemAxisEqD0(I.ItemPointer, 1000); } DfsDLLWrapper.dfsFileCreate(FileName, _headerPointer, out _filePointer); } protected void WriteGeoInfo() { if (!_initializedForWriting) InitializeForWriting(); DfsDLLWrapper.dfsSetGeoInfoUTMProj(_headerPointer, "NON-UTM", _xOrigin, _yOrigin, _orientation); foreach (Item I in Items) { WriteItemInfo(I); if (_spaceAxis == SpaceAxisType.EqD2) DfsDLLWrapper.dfsSetItemAxisEqD2(I.ItemPointer, 1000, _numberOfColumns, _numberOfRows, 0, 0, (float)_gridSize, (float)_gridSize); } } internal void WriteItemInfo(Item I) { if (!_initializedForWriting) InitializeForWriting(); DfsDLLWrapper.dfsSetItemInfo(_headerPointer, I.ItemPointer, (int)I.EumItem, I.Name, (int)I.EumUnit, DfsSimpleType.Float); DfsDLLWrapper.dfsSetItemValueType(I.ItemPointer, I.ValueType); } /// /// Writes timestep and starttime /// protected void WriteTime() { if (!_initializedForWriting) InitializeForWriting(); switch (_timeAxis) { case TimeAxisType.CalendarEquidistant: DfsDLLWrapper.dfsSetEqCalendarAxis(_headerPointer, TimeSteps.First().ToString("yyyy-MM-dd"), TimeSteps.First().ToString("HH:mm:ss"), (int)timeStepUnit, 0, _timeStep.TotalSeconds, 0); break; case TimeAxisType.CalendarNonEquidistant: DfsDLLWrapper.dfsSetNeqCalendarAxis(_headerPointer, TimeSteps.First().ToString("yyyy-MM-dd"), TimeSteps.First().ToString("HH:mm:ss"), (int)timeStepUnit, 0, 0); break; case TimeAxisType.TimeEquidistant: break; case TimeAxisType.TimeNonEquidistant: break; case TimeAxisType.Undefined: break; default: break; } } #endregion #region Properties private Item[] items; /// /// Gets the items /// public Item[] Items { get { if (items == null) { items = new Item[NumberOfItems]; //Gets the pointers to the items for (int i = 0; i < NumberOfItems; i++) { items[i] = new Item(DfsDLLWrapper.dfsItemD(_headerPointer, i + 1), this, i + 1); } } return items; } } /// /// Gets the status code from the last call by DFSWrapper /// protected int LastStatus { get { return _status; } set { _status = value; if (_status != 0) { string error = "fejl"; } } } /// /// Gets the first item. There should always be at least one item /// public Item FirstItem { get { return Items[0]; } } /// /// Gets an array with the timesteps. /// public virtual IList TimeSteps { get { return _timesteps; } } /// /// Gets and sets the date and time of the first time step. /// public DateTime TimeOfFirstTimestep { get { return TimeSteps.First(); } set { if (TimeSteps.Count == 0) TimeSteps.Add(value); else TimeSteps[0] = value; WriteTime(); } } /// /// Gets and sets the size of a time step /// public TimeSpan TimeStep { get { return _timeStep; } set { if (_timeStep != value) { _timeStep = value; WriteTime(); } } } /// /// Gets the DeleteValue from the DFS-file /// public double DeleteValue { get { if (!_deleteValue.HasValue) { _deleteValue = DfsDLLWrapper.dfsGetDeleteValFloat(_headerPointer); } return _deleteValue.Value; } set { if (_deleteValue.HasValue) if (_deleteValue.Value == value) return; _deleteValue = value; DfsDLLWrapper.dfsSetDeleteValFloat(_headerPointer, (float)value); } } /// /// Gets the FileName /// public string FileName { get { return _filename; } } /// /// Gets the number of timesteps /// public int NumberOfTimeSteps { get { return TimeSteps.Count; } } /// /// Gets the number of items /// public int NumberOfItems { get; private set; } #endregion #region Dispose methods /// /// Override of the Dispose method in DFSFileInfo which probably does not account for finalization /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { dfsdata = null; } if (_headerPointer != IntPtr.Zero) DfsDLLWrapper.dfsFileClose(_headerPointer, ref _filePointer); } /// /// Destructor called when the object is garbage collected. /// ~DFSBase() { // Simply call Dispose(false). Dispose(false); } #endregion } public class Item { internal IntPtr ItemPointer { get; set; } private eumUnit _eumUnit = eumUnit.eumUUnitUndefined; private string eumUnitString; private string _name; private eumItem _eumitem = eumItem.eumIItemUndefined; private DFSBase _dfs; private DataValueType valueType; public int ItemNumber { get; private set; } public int NumberOfElements { get; private set; } internal Item(IntPtr ItemPointer, DFSBase DFS, int Number) { ItemNumber = Number; _dfs = DFS; int item_type = 0; int data_type = 0; int value_type = 0; IntPtr name = new IntPtr(); IntPtr Eum = new IntPtr(); this.ItemPointer = ItemPointer; DfsDLLAccess.dfsGetItemInfo_(ItemPointer, out item_type, ref name, ref Eum, out data_type); DfsDLLAccess.dfsGetItemValueType(ItemPointer, out value_type); NumberOfElements = (int)DfsDLLAccess.dfsGetItemElements(ItemPointer); valueType = (DataValueType)value_type; _name = (Marshal.PtrToStringAnsi(name)); eumUnitString = Marshal.PtrToStringAnsi(Eum); if (item_type != 0) _eumitem = (eumItem)item_type; } /// /// Gets and sets the name /// public string Name { get { return _name; } set { if (value != _name) { _name = value; _dfs.WriteItemInfo(this); } } } /// /// Gets and sets the Eum Item /// public eumItem EumItem { get { return _eumitem; } set { if (_eumitem != value) { _eumitem = value; _eumUnit = PossibleUnits.First(); _dfs.WriteItemInfo(this); } } } /// /// Gets a list of units possible for the selected EUM Item /// public eumUnit[] PossibleUnits { get { return EUMWrapper.GetItemAllowedUnits(EumItem); } } /// /// Gets the eum quantity /// public eumQuantity EumQuantity { get { return new eumQuantity(EumItem, EumUnit); } } /// /// Gets and sets the eum unit. /// Note that possible units depend on the EUMItem. Setting to an impossible unit will set the unit to first possible type /// public eumUnit EumUnit { get { if (_eumUnit == eumUnit.eumUUnitUndefined) { int u = 0; bool w = EUMWrapper.GetUnitTag(this.eumUnitString, out u); //This call is very expensive. _eumUnit = (eumUnit)u; } return _eumUnit; } set { if (!PossibleUnits.Contains(value)) _eumUnit = PossibleUnits.First(); _eumUnit = value; } } /// /// Gets and sets the data value type /// public DataValueType ValueType { get { return valueType; } set { if (valueType != value) { valueType = value; _dfs.WriteItemInfo(this); } } } public override string ToString() { return Name; } } }