Compare commits

...

25 Commits

Author SHA1 Message Date
Chris Cameron
0a1f637b45 chore: Updating changelog, incrementing patch version 2019-10-29 14:52:36 -04:00
Chris Cameron
e0ace12ef9 Merge pull request #43 from Utils fix/SerialQueueFix
Fix/SerialQueueFix
2019-10-29 14:33:28 -04:00
Jack Kanarish
9f1541f843 chore: update changelog 2019-10-29 14:19:58 -04:00
Jack Kanarish
b9f5349055 fix: fix an issue where commands would collapse to the point of the last deleted command instead of the position in queue where the new command was going 2019-10-29 14:18:45 -04:00
Chris Cameron
5900f5974b chore: Updating changelog, incrementing patch version 2019-08-22 10:41:00 -04:00
Drew Tingen
81f7eb9c66 Merge branch 'fix/ordered-dict' of Common/Utils into MetLife_v5.4.3 2019-08-22 14:30:40 +00:00
Chris Cameron
440c1c62f1 fix: Fixed a bug with the IcdOrderedDict index setter that was creating additional values 2019-08-22 10:17:32 -04:00
Chris Cameron
e388f35efb Revert "fix: fix for logging deadlock, use mutexes, add logging for last message sent."
This reverts commit 7c238b9fef.
2019-08-15 10:56:34 -04:00
Jack Kanarish
89061b5cbf feat: add unit test for timer 2019-08-07 20:47:54 -04:00
Jack Kanarish
7c238b9fef fix: fix for logging deadlock, use mutexes, add logging for last message sent. 2019-07-31 16:22:05 -04:00
Chris Cameron
1be852685d chore: Updating changelog, incrementing minor version 2019-06-24 12:49:39 -04:00
Chris Cameron
6201184fab feat: IcdXmlException exposes LineNumber and LinePosition 2019-06-24 12:42:29 -04:00
Chris Cameron
b915401109 chore: Updating changelog, incrementing patch version 2019-06-14 16:52:56 -04:00
Austin Noska
3e3a4e33cb fix: SafeTimer Stopped now returns a SafeTimer with no due time and no repeat period 2019-06-14 16:51:36 -04:00
Chris Cameron
7666021925 chore: Updating changelog, incrementing minor version 2019-06-14 10:25:47 -04:00
Chris Cameron
7b6092c291 fix: Fixing issues with Remap methods, added unit tests 2019-06-13 17:35:03 -04:00
Chris Cameron
f7740aaea2 fix: Fixing overflow 2019-06-13 17:10:34 -04:00
Chris Cameron
c31b2b556b feat: Adding methods for remapping and clamping numeric values to ranges 2019-06-13 16:56:49 -04:00
Chris Cameron
8866a3e1e6 fix: Fixing tests, bad maths 2019-06-13 11:59:12 -04:00
Chris Cameron
e3273934a7 fix: Begin rewriting RangeAttribute 2019-06-12 23:42:59 -04:00
Chris Cameron
6526b2b811 chore: Updating changelog, incrementing minor version 2019-06-06 15:52:35 -04:00
Chris Cameron
ead365e251 chore: Fixing bad merge 2019-06-06 15:51:53 -04:00
Jack Kanarish
f24e6d2597 Merge branch 'feat/init-event' into MetLife_v5.4.2
# Conflicts:
#	CHANGELOG.md
2019-06-06 11:06:09 -04:00
Chris Cameron
4953e40f9e docs: Updating changelog 2019-05-31 13:34:29 -04:00
Chris Cameron
69f872ced1 feat: Adding features to IcdEnvironment for tracking program initialization state 2019-05-31 13:33:21 -04:00
13 changed files with 618 additions and 276 deletions

View File

@@ -6,6 +6,30 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased]
## [8.7.2] - 2019-10-29
### Changed
- Fixed a bug with PriorityQueue de-duplication where a new command would be inserted in the wrong position
## [8.7.1] - 2019-08-22
### Changed
- Fixed a bug with the IcdOrderedDict index setter that was creating additional values
## [8.7.0] - 2019-06-24
### Added
- IcdXmlException exposes line number and position properties
## [8.6.1] - 2019-06-14
### Changed
- Fixed a bug where stopped timers on NetStandard would still have a periodic callback duration
## [8.6.0] - 2019-06-14
### Changed
- Overhaul of RangeAttribute remap methods to better avoid overflows
## [8.5.0] - 2019-06-06
### Added
- Adding features to IcdEnvironment for tracking program initialization state
## [8.4.1] - 2019-06-05
### Changed
- Caching the program/processor start time and calculating the uptime from those values instead of polling the crestron processor

View File

@@ -0,0 +1,86 @@
using System;
using NUnit.Framework;
using RangeAttribute = ICD.Common.Utils.Attributes.RangeAttribute;
namespace ICD.Common.Utils.Tests.Attributes
{
[TestFixture]
public sealed class RangeAttributeTest : AbstractIcdAttributeTest<RangeAttribute>
{
#region Properties
[TestCase(1)]
[TestCase(1.0f)]
[TestCase(1.0)]
public void MinTest(object min)
{
Assert.AreEqual(min, new RangeAttribute(min, min).Min);
}
[TestCase(1)]
[TestCase(1.0f)]
[TestCase(1.0)]
public void MaxTest(object max)
{
Assert.AreEqual(max, new RangeAttribute(max, max).Max);
}
#endregion
#region Methods
[TestCase((double)0, (double)0)]
[TestCase((double)1, (double)1)]
[TestCase(ushort.MaxValue, double.MaxValue)]
[TestCase(short.MinValue, double.MinValue)]
public void RemapToDoubleTest(object value, double expected)
{
Assert.AreEqual(expected, RangeAttribute.RemapToDouble(value));
}
[TestCase((double)0, typeof(ushort), (ushort)32767)]
[TestCase(double.MinValue, typeof(ushort), ushort.MinValue)]
[TestCase(double.MaxValue, typeof(ushort), ushort.MaxValue)]
public void RemapFromDoubleTest(double value, Type type, object expected)
{
Assert.AreEqual(expected, RangeAttribute.RemapFromDouble(value, type));
}
[TestCase(short.MinValue, typeof(ushort), ushort.MinValue)]
[TestCase(short.MaxValue, typeof(ushort), short.MaxValue)]
public static void Clamp(object value, Type type, object expected)
{
Assert.AreEqual(expected, RangeAttribute.Clamp(value, type));
}
[TestCase(double.MinValue, typeof(ushort), ushort.MinValue)]
[TestCase(double.MaxValue, typeof(ushort), ushort.MaxValue)]
public void Clamp(double value, Type type, double expected)
{
Assert.AreEqual(expected, RangeAttribute.Clamp(value, type));
}
[TestCase(short.MinValue, typeof(ushort), ushort.MinValue)]
[TestCase(short.MaxValue, typeof(ushort), ushort.MaxValue)]
public void RemapTest(object value, Type type, object expected)
{
Assert.AreEqual(expected, RangeAttribute.Remap(value, type));
}
[TestCase(0, 100, ushort.MaxValue, typeof(ushort), ushort.MaxValue)]
[TestCase(0, 100, ushort.MaxValue, typeof(short), short.MaxValue)]
public void ClampMinMaxThenRemapTest(object min, object max, object value, Type type, object expected)
{
Assert.AreEqual(expected, new RangeAttribute(min, max).ClampMinMaxThenRemap(value, type));
}
[TestCase(0, 100, ushort.MaxValue, 100)]
[TestCase(0, 100, ushort.MinValue, 0)]
public void RemapMinMaxTest(object min, object max, object value, object expected)
{
Assert.AreEqual(expected, new RangeAttribute(min, max).RemapMinMax(value));
}
#endregion
}
}

View File

@@ -69,16 +69,26 @@ namespace ICD.Common.Utils.Tests.Collections
[Test]
public void IndexerTest()
{
IcdOrderedDictionary<int, int> dict = new IcdOrderedDictionary<int, int>
{
{0, 0},
{1, 10},
{-1, -10}
};
// ReSharper disable UseObjectOrCollectionInitializer
IcdOrderedDictionary<int, int> dict = new IcdOrderedDictionary<int, int>();
// ReSharper restore UseObjectOrCollectionInitializer
dict[0] = 0;
dict[1] = 10;
dict[-1] = -10;
dict[-1] = -11;
Assert.AreEqual(0, dict[0]);
Assert.AreEqual(10, dict[1]);
Assert.AreEqual(-10, dict[-1]);
Assert.AreEqual(-11, dict[-1]);
Assert.AreEqual(3, dict.Count);
int[] expectedKeys = {-1, 0, 1 };
int[] expectedValues = {-11, 0, 10};
Assert.AreEqual(expectedKeys, dict.Keys.ToArray());
Assert.AreEqual(expectedValues, dict.Values.ToArray());
}
#endregion

View File

@@ -27,6 +27,10 @@ namespace ICD.Common.Utils.Tests
[Test, UsedImplicitly]
public void MapRangeTest()
{
Assert.AreEqual(5, MathUtils.MapRange(-100, 100, 0, 10, 0));
Assert.AreEqual(7, MathUtils.MapRange(-100, 100, 0, 10, 50));
Assert.AreEqual(10, MathUtils.MapRange(-100, 100, 0, 10, 100));
Assert.AreEqual(0, MathUtils.MapRange(0, 100, 0, 10, 0));
Assert.AreEqual(5, MathUtils.MapRange(0, 100, 0, 10, 50));
Assert.AreEqual(10, MathUtils.MapRange(0, 100, 0, 10, 100));
@@ -36,7 +40,7 @@ namespace ICD.Common.Utils.Tests
Assert.AreEqual(100, MathUtils.MapRange(0, 10, 0, 100, 10));
}
[Test, UsedImplicitly]
[Test, UsedImplicitly]
public void GetRangesTest()
{
IEnumerable<int> values = new [] { 1, 3, 5, 6, 7, 8, 9, 10, 12 };

View File

@@ -0,0 +1,17 @@
using ICD.Common.Utils.Timers;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Timers
{
[TestFixture]
public sealed class IcdStopwatchTest
{
[Test]
public void ConstructorTest()
{
var stopwatch = new IcdStopwatch();
ThreadingUtils.Sleep(100);
Assert.AreEqual(0, stopwatch.ElapsedMilliseconds);
}
}
}

View File

@@ -1,4 +1,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.Attributes
{
@@ -8,295 +11,509 @@ namespace ICD.Common.Utils.Attributes
[AttributeUsage(AttributeTargets.Property |
AttributeTargets.Field |
AttributeTargets.Parameter |
AttributeTargets.ReturnValue,
AllowMultiple = false,
Inherited = true)]
AttributeTargets.ReturnValue)]
public sealed class RangeAttribute : AbstractIcdAttribute
{
/// <summary>
/// Remaps from the source numeric min/max to double min/max.
/// </summary>
private static readonly Dictionary<Type, Func<double, double>> s_Clamp =
new Dictionary<Type, Func<double, double>>
{
// Duh
{typeof(double), o => o},
// Signed
{typeof(short), o => o < short.MinValue ? short.MinValue : o > short.MaxValue ? short.MaxValue : o},
{typeof(int), o => o < int.MinValue ? int.MinValue : o > int.MaxValue ? int.MaxValue : o},
{typeof(long), o => o < long.MinValue ? long.MinValue : o > long.MaxValue ? long.MaxValue : o},
{typeof(float),o => o < float.MinValue ? float.MinValue : o > float.MaxValue ? float.MaxValue : o},
{typeof(decimal), o => o < (double)decimal.MinValue ? (double)decimal.MinValue : o > (double)decimal.MaxValue ? (double)decimal.MaxValue : o},
// Unsigned
{typeof(ushort), o => o < ushort.MinValue ? ushort.MinValue : o > ushort.MaxValue ? ushort.MaxValue : o},
{typeof(uint), o => o < uint.MinValue ? uint.MinValue : o > uint.MaxValue ? uint.MaxValue : o},
{typeof(ulong), o => o < ulong.MinValue ? ulong.MinValue : o > ulong.MaxValue ? ulong.MaxValue : o},
{typeof(byte), o => o < byte.MinValue ? byte.MinValue : o > byte.MaxValue ? byte.MaxValue : o}
};
/// <summary>
/// Remaps from the source numeric min/max to double min/max.
/// </summary>
private static readonly Dictionary<Type, Func<object, double>> s_RemapToDouble =
new Dictionary<Type, Func<object, double>>
{
// Duh
{ typeof(double), o => (double)o},
// Signed - Clamping prevents an overflow due to loss of precision
{ typeof(short), o => MathUtils.Clamp(Convert.ToDouble(o) / short.MaxValue, -1, 1) * double.MaxValue},
{ typeof(int), o => MathUtils.Clamp(Convert.ToDouble(o) / int.MaxValue, -1, 1) * double.MaxValue},
{ typeof(long), o => MathUtils.Clamp(Convert.ToDouble(o) / long.MaxValue, -1, 1) * double.MaxValue},
{ typeof(float), o => MathUtils.Clamp(Convert.ToDouble(o) / float.MaxValue, -1, 1) * double.MaxValue},
{ typeof(decimal), o => MathUtils.Clamp(Convert.ToDouble(o) / (double)decimal.MaxValue, -1, 1) * double.MaxValue},
// Unsigned
{ typeof(ushort), o => MathUtils.Clamp((Convert.ToDouble(o) / ushort.MaxValue - 0.5) * 2, -1, 1) * double.MaxValue},
{ typeof(uint), o => MathUtils.Clamp((Convert.ToDouble(o) / uint.MaxValue - 0.5) * 2, -1, 1) * double.MaxValue},
{ typeof(ulong), o => MathUtils.Clamp((Convert.ToDouble(o) / ulong.MaxValue - 0.5) * 2, -1, 1) * double.MaxValue},
{ typeof(byte), o => MathUtils.Clamp((Convert.ToDouble(o) / byte.MaxValue - 0.5) * 2, -1, 1) * double.MaxValue}
};
/// <summary>
/// Remaps from the double min/max to target numeric min/max.
/// </summary>
private static readonly Dictionary<Type, Func<double, object>> s_RemapFromDouble =
new Dictionary<Type, Func<double, object>>
{
// Duh
{typeof(double), v => v},
// Signed
{typeof(short), v => (short)(v / double.MaxValue * short.MaxValue)},
{typeof(int), v => (int)(v / double.MaxValue * int.MaxValue)},
{typeof(long), v => (long)(v / double.MaxValue * long.MaxValue)},
{typeof(float), v => (float)(v / double.MaxValue * float.MaxValue)},
{typeof(decimal), v => (decimal)(v / double.MaxValue) * decimal.MaxValue},
// Unsigned
{typeof(ushort), v => (ushort)((v / double.MaxValue + 1) / 2 * ushort.MaxValue)},
{typeof(uint), v => (uint)((v / double.MaxValue + 1) / 2 * uint.MaxValue)},
{typeof(ulong), v => (ulong)((v / double.MaxValue + 1) / 2 * ulong.MaxValue)},
{typeof(byte), v => (byte)((v / double.MaxValue + 1) / 2 * byte.MaxValue)}
};
/// <summary>
/// Gets the min value of a given numeric type as a double.
/// </summary>
private static readonly Dictionary<Type, double> s_MinAsDouble =
new Dictionary<Type, double>
{
// Duh
{typeof(double), double.MinValue},
// Signed
{typeof(short), Convert.ToDouble(short.MinValue)},
{typeof(int), Convert.ToDouble(int.MinValue)},
{typeof(long), Convert.ToDouble(long.MinValue)},
{typeof(float), Convert.ToDouble(float.MinValue)},
{typeof(decimal), Convert.ToDouble(decimal.MinValue)},
// Unsigned
{typeof(ushort), Convert.ToDouble(ushort.MinValue)},
{typeof(uint), Convert.ToDouble(uint.MinValue)},
{typeof(ulong), Convert.ToDouble(ulong.MinValue)},
{typeof(byte), Convert.ToDouble(byte.MinValue)}
};
/// <summary>
/// Gets the min value of a given numeric type as a double.
/// </summary>
private static readonly Dictionary<Type, double> s_MaxAsDouble =
new Dictionary<Type, double>
{
// Duh
{typeof(double), double.MaxValue},
// Signed
{typeof(short), Convert.ToDouble(short.MaxValue)},
{typeof(int), Convert.ToDouble(int.MaxValue)},
{typeof(long), Convert.ToDouble(long.MaxValue)},
{typeof(float), Convert.ToDouble(float.MaxValue)},
{typeof(decimal), Convert.ToDouble(decimal.MaxValue)},
// Unsigned
{typeof(ushort), Convert.ToDouble(ushort.MaxValue)},
{typeof(uint), Convert.ToDouble(uint.MaxValue)},
{typeof(ulong), Convert.ToDouble(ulong.MaxValue)},
{typeof(byte), Convert.ToDouble(byte.MaxValue)}
};
private readonly object m_Min;
private readonly object m_Max;
#region Properties
public object Min { get; private set; }
public object Max { get; private set; }
/// <summary>
/// Gets the min value for this range.
/// </summary>
public object Min { get { return m_Min; } }
/// <summary>
/// Gets the max value for this range.
/// </summary>
public object Max { get { return m_Max; } }
#endregion
#region Constructors
/// <summary>
/// Constructor.
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
public RangeAttribute(ushort min, ushort max)
: this((object)min, (object)max)
{
Min = min;
Max = max;
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
public RangeAttribute(short min, short max)
: this((object)min, (object)max)
{
Min = min;
Max = max;
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
public RangeAttribute(uint min, uint max)
: this((object)min, (object)max)
{
Min = min;
Max = max;
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
public RangeAttribute(int min, int max)
: this((object)min, (object)max)
{
Min = min;
Max = max;
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
public RangeAttribute(ulong min, ulong max)
: this((object)min, (object)max)
{
Min = min;
Max = max;
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
public RangeAttribute(long min, long max)
: this((object)min, (object)max)
{
Min = min;
Max = max;
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
public RangeAttribute(float min, float max)
: this((object)min, (object)max)
{
Min = min;
Max = max;
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
public RangeAttribute(double min, double max)
: this((object)min, (object)max)
{
Min = min;
Max = max;
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
public RangeAttribute(byte min, byte max)
: this((object)min, (object)max)
{
Min = min;
Max = max;
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
public RangeAttribute(sbyte min, sbyte max)
: this((object)min, (object)max)
{
Min = min;
Max = max;
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
public RangeAttribute(decimal min, decimal max)
: this((object)min, (object)max)
{
Min = min;
Max = max;
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
public RangeAttribute(object min, object max)
{
if (min == null)
throw new ArgumentNullException("min");
if (max == null)
throw new ArgumentNullException("max");
if (min.GetType() != max.GetType())
throw new ArgumentException("Min and Max types do not match");
if (!min.GetType().IsNumeric())
throw new ArgumentException("Given types are not numeric");
m_Min = min;
m_Max = max;
}
#endregion
#region Methods
public T GetMin<T>()
/// <summary>
/// Remaps the given numeric value from its min/max range into double min/max range.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static double RemapToDouble(object value)
{
return (T)Convert.ChangeType(Min, typeof(T), null);
if (value == null)
throw new ArgumentNullException("value");
Func<object, double> remap;
if (!s_RemapToDouble.TryGetValue(value.GetType(), out remap))
throw new NotSupportedException("Value type is not supported.");
return remap(value);
}
public T GetMax<T>()
/// <summary>
/// Remaps the given double value from its min/max range into the target type min/max range.
/// </summary>
/// <param name="value"></param>
/// <param name="type"></param>
/// <returns></returns>
public static object RemapFromDouble(double value, Type type)
{
return (T)Convert.ChangeType(Max, typeof(T), null);
if (type == null)
throw new ArgumentNullException("type");
Func<double, object> remap;
if (!s_RemapFromDouble.TryGetValue(type, out remap))
throw new NotSupportedException("Value type is not supported.");
return remap(value);
}
public bool IsInRange(object value)
/// <summary>
/// Clamps the given numeric value into the valid ranges of the target numeric type.
/// </summary>
/// <param name="value"></param>
/// <param name="type"></param>
/// <returns></returns>
public static object Clamp(object value, Type type)
{
if (value is ushort)
{
if (!(Min is ushort))
throw new ArgumentException("the type of value does not match the type of min / max");
if (value == null)
throw new ArgumentNullException("value");
var castVal = (ushort)value;
return (castVal >= GetMin<ushort>() && castVal <= GetMax<ushort>());
}
if (type == null)
throw new ArgumentNullException("type");
if (value is short)
{
if (!(Min is short))
throw new ArgumentException("the type of value does not match the type of min / max");
if (!type.IsNumeric())
throw new ArgumentException("Target type is not numeric");
var castVal = (short)value;
return (castVal >= GetMin<short>() && castVal <= GetMax<short>());
}
if (!value.GetType().IsNumeric())
throw new ArgumentException("Source value is not numeric");
if (value is uint)
{
if (!(Min is uint))
throw new ArgumentException("the type of value does not match the type of min / max");
double doubleValue = Convert.ToDouble(value);
double clamped = Clamp(doubleValue, type);
var castVal = (uint)value;
return (castVal >= GetMin<uint>() && castVal <= GetMax<uint>());
}
if (value is int)
{
if (!(Min is int))
throw new ArgumentException("the type of value does not match the type of min / max");
var castVal = (int)value;
return (castVal >= GetMin<int>() && castVal <= GetMax<int>());
}
if (value is ulong)
{
if (!(Min is ulong))
throw new ArgumentException("the type of value does not match the type of min / max");
var castVal = (ulong)value;
return (castVal >= GetMin<ulong>() && castVal <= GetMax<ulong>());
}
if (value is long)
{
if (!(Min is long))
throw new ArgumentException("the type of value does not match the type of min / max");
var castVal = (long)value;
return (castVal >= GetMin<long>() && castVal <= GetMax<long>());
}
if (value is float)
{
if (!(Min is float))
throw new ArgumentException("the type of value does not match the type of min / max");
var castVal = (float)value;
return (castVal >= GetMin<float>() && castVal <= GetMax<float>());
}
if (value is double)
{
if (!(Min is double))
throw new ArgumentException("the type of value does not match the type of min / max");
var castVal = (double)value;
return (castVal >= GetMin<double>() && castVal <= GetMax<double>());
}
if (value is decimal)
{
if (!(Min is decimal))
throw new ArgumentException("the type of value does not match the type of min / max");
var castVal = (decimal)value;
return (castVal >= GetMin<decimal>() && castVal <= GetMax<decimal>());
}
if (value is byte)
{
if (!(Min is byte))
throw new ArgumentException("the type of value does not match the type of min / max");
var castVal = (byte)value;
return (castVal >= GetMin<byte>() && castVal <= GetMax<byte>());
}
if (value is sbyte)
{
if (!(Min is sbyte))
throw new ArgumentException("the type of value does not match the type of min / max");
var castVal = (sbyte)value;
return (castVal >= GetMin<sbyte>() && castVal <= GetMax<sbyte>());
}
throw new ArgumentException("the type of value is not a numeric type.");
return Convert.ChangeType(clamped, value.GetType(), CultureInfo.InvariantCulture);
}
#region Range -> UShort
public ushort RemapRangeToUshort(double value)
/// <summary>
/// Clamps the given double value into the valid ranges of the target numeric type.
/// </summary>
/// <param name="value"></param>
/// <param name="type"></param>
/// <returns></returns>
public static double Clamp(double value, Type type)
{
return (ushort)MathUtils.MapRange(GetMin<double>(), GetMax<double>(), ushort.MinValue, ushort.MaxValue, value);
if (type == null)
throw new ArgumentNullException("type");
Func<double, double> clamp;
if (!s_Clamp.TryGetValue(type, out clamp))
throw new NotSupportedException("Value type is not supported.");
return clamp(value);
}
public ushort RemapRangeToUshort(float value)
/// <summary>
/// Remaps the numeric value into the min-max range of the target numeric type.
/// </summary>
/// <param name="value"></param>
/// <param name="type"></param>
/// <returns></returns>
public static object Remap(object value, Type type)
{
return (ushort)MathUtils.MapRange(GetMin<float>(), GetMax<float>(), ushort.MinValue, ushort.MaxValue, value);
if (value == null)
throw new ArgumentNullException("value");
if (type == null)
throw new ArgumentNullException("type");
if (!type.IsNumeric())
throw new ArgumentException("Target type is not numeric");
if (!value.GetType().IsNumeric())
throw new ArgumentException("Source value is not numeric");
double intermediate = RemapToDouble(value);
return RemapFromDouble(intermediate, type);
}
public ushort RemapRangeToUshort(int value)
/// <summary>
/// Clamps the given numeric value to the defined min/max then remaps to the target numeric type.
/// </summary>
/// <param name="value"></param>
/// <param name="type"></param>
/// <returns></returns>
public object ClampMinMaxThenRemap(object value, Type type)
{
return (ushort)MathUtils.MapRange(GetMin<int>(), GetMax<int>(), ushort.MinValue, ushort.MaxValue, value);
if (value == null)
throw new ArgumentNullException("value");
if (type == null)
throw new ArgumentNullException("type");
if (!type.IsNumeric())
throw new ArgumentException("Target type is not numeric");
if (!value.GetType().IsNumeric())
throw new ArgumentException("Source value is not numeric");
double min = Convert.ToDouble(Min);
double max = Convert.ToDouble(Max);
double doubleValue = Convert.ToDouble(value);
double clamped = MathUtils.Clamp(doubleValue, min, max);
object remapped = RemapMinMax(clamped, type);
return Convert.ChangeType(remapped, value.GetType(), CultureInfo.InvariantCulture);
}
public ushort RemapRangeToUshort(ushort value)
/// <summary>
/// Remaps the given numeric value to the defined min/max.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public object RemapMinMax(object value)
{
return MathUtils.MapRange(GetMin<ushort>(), GetMax<ushort>(), ushort.MinValue, ushort.MaxValue, value);
if (value == null)
throw new ArgumentNullException("value");
if (!value.GetType().IsNumeric())
throw new ArgumentException("Source value is not numeric");
double sourceMin = GetMinAsDouble(value.GetType());
double sourceMax = GetMaxAsDouble(value.GetType());
double targetMin = Convert.ToDouble(Min);
double targetMax = Convert.ToDouble(Max);
double doubleValue = Convert.ToDouble(value);
double remapped = MathUtils.MapRange(sourceMin, sourceMax, targetMin, targetMax, doubleValue);
return Convert.ChangeType(remapped, value.GetType(), CultureInfo.InvariantCulture);
}
private object RemapMinMax(object value, Type type)
{
if (value == null)
throw new ArgumentNullException("value");
if (type == null)
throw new ArgumentNullException("type");
if (!type.IsNumeric())
throw new ArgumentException("Target type is not numeric");
if (!value.GetType().IsNumeric())
throw new ArgumentException("Source value is not numeric");
double sourceMin = Convert.ToDouble(Min);
double sourceMax = Convert.ToDouble(Max);
double targetMin = GetMinAsDouble(type);
double targetMax = GetMaxAsDouble(type);
double doubleValue = Convert.ToDouble(value);
double remapped = MathUtils.MapRange(sourceMin, sourceMax, targetMin, targetMax, doubleValue);
return Convert.ChangeType(remapped, type, CultureInfo.InvariantCulture);
}
#endregion
#region UShort -> Range
#region Private Methods
public object RemapUshortToRange(ushort value)
/// <summary>
/// Gets the min value for the given numeric type as a double.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private static double GetMinAsDouble(Type type)
{
if (Min is ushort)
{
return MathUtils.MapRange(ushort.MinValue, ushort.MaxValue, GetMin<ushort>(), GetMax<ushort>(), value);
}
if (type == null)
throw new ArgumentNullException("type");
if (Min is short)
{
var castVal = (short)value;
return (short)MathUtils.MapRange(ushort.MinValue, ushort.MaxValue, GetMin<short>(), GetMax<short>(), castVal);
}
if (!type.IsNumeric())
throw new ArgumentException("Target type is not numeric");
if (Min is uint)
{
var castVal = (uint)value;
return (uint)MathUtils.MapRange(ushort.MinValue, ushort.MaxValue, GetMin<uint>(), GetMax<uint>(), castVal);
}
double min;
if (!s_MinAsDouble.TryGetValue(type, out min))
throw new NotSupportedException("Type is not supported.");
if (Min is int)
{
var castVal = (int)value;
return MathUtils.MapRange(ushort.MinValue, ushort.MaxValue, GetMin<int>(), GetMax<int>(), castVal);
}
if (Min is ulong)
{
var castVal = (ulong)value;
return MathUtils.MapRange(ushort.MinValue, ushort.MaxValue, GetMin<ulong>(), GetMax<ulong>(), castVal);
}
if (Min is long)
{
var castVal = (long)value;
return MathUtils.MapRange(ushort.MinValue, ushort.MaxValue, GetMin<long>(), GetMax<long>(), castVal);
}
if (Min is float)
{
var castVal = (float)value;
return MathUtils.MapRange(ushort.MinValue, ushort.MaxValue, GetMin<float>(), GetMax<float>(), castVal);
}
if (Min is double)
{
var castVal = (double)value;
return MathUtils.MapRange(ushort.MinValue, ushort.MaxValue, GetMin<double>(), GetMax<double>(), castVal);
}
if (Min is decimal)
{
var castVal = (decimal)value;
return MathUtils.MapRange(ushort.MinValue, ushort.MaxValue, GetMin<decimal>(), GetMax<decimal>(), castVal);
}
if (Min is byte)
{
var castVal = (byte)value;
return (byte)MathUtils.MapRange(ushort.MinValue, ushort.MaxValue, GetMin<byte>(), GetMax<byte>(), castVal);
}
throw new NotSupportedException("Value type of range attribute is not supported.");
return min;
}
#endregion
/// <summary>
/// Gets the max value for the given numeric type as a double.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private static double GetMaxAsDouble(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
if (!type.IsNumeric())
throw new ArgumentException("Target type is not numeric");
double max;
if (!s_MaxAsDouble.TryGetValue(type, out max))
throw new NotSupportedException("Type is not supported.");
return max;
}
#endregion
}

View File

@@ -32,13 +32,8 @@ namespace ICD.Common.Utils.Collections
if (key == null)
throw new ArgumentNullException("key");
if (!ContainsKey(key))
{
int index = m_OrderedKeys.AddSorted(key, m_Comparer);
m_ValuesOrderedByKey.Insert(index, value);
}
m_Dictionary[key] = value;
Remove(key);
Add(key, value);
}
}
@@ -109,9 +104,12 @@ namespace ICD.Common.Utils.Collections
throw new ArgumentNullException("key");
if (m_Dictionary.ContainsKey(key))
throw new ArgumentException("An item with the same key has already been added.", "key");
throw new ArgumentOutOfRangeException("key", "An item with the same key has already been added.");
this[key] = value;
int index = m_OrderedKeys.AddSorted(key, m_Comparer);
m_ValuesOrderedByKey.Insert(index, value);
m_Dictionary[key] = value;
}
public void Clear()

View File

@@ -134,8 +134,6 @@ namespace ICD.Common.Utils.Collections
if (remove == null)
throw new ArgumentNullException("remove");
bool inserted = false;
foreach (KeyValuePair<int, List<T>> kvp in m_PriorityToQueue.ToArray())
{
int[] removeIndices =
@@ -144,35 +142,17 @@ namespace ICD.Common.Utils.Collections
.Reverse()
.ToArray();
if (removeIndices.Length == 0)
continue;
foreach (int removeIndex in removeIndices)
{
kvp.Value.RemoveAt(removeIndex);
m_Count--;
}
if (!inserted)
{
int insertIndex = removeIndices[0];
if (insertIndex >= kvp.Value.Count)
kvp.Value.Add(item);
else
kvp.Value.Insert(insertIndex, item);
m_Count++;
inserted = true;
}
if (kvp.Value.Count == 0)
m_PriorityToQueue.Remove(kvp.Key);
}
if (!inserted)
Enqueue(item, priority);
Enqueue(item, priority);
}
/// <summary>

View File

@@ -1,4 +1,6 @@
using System;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils
{
@@ -54,5 +56,41 @@ namespace ICD.Common.Utils
public static event ProgramStatusCallback OnProgramStatusEvent;
public static event EthernetEventCallback OnEthernetEvent;
/// <summary>
/// Raised when the program has completed initialization.
/// </summary>
public static event EventHandler OnProgramInitializationComplete;
private static readonly SafeCriticalSection s_ProgramInitializationSection = new SafeCriticalSection();
private static bool s_ProgramInitializationComplete;
/// <summary>
/// Returns true if the program has been flagged as completely initialized.
/// </summary>
public static bool ProgramIsInitialized { get { return s_ProgramInitializationSection.Execute(() => s_ProgramInitializationComplete); } }
/// <summary>
/// Called by the program entry point to signify that the program initialization is complete.
/// </summary>
[PublicAPI]
public static void SetProgramInitializationComplete()
{
s_ProgramInitializationSection.Enter();
try
{
if (s_ProgramInitializationComplete)
return;
s_ProgramInitializationComplete = true;
}
finally
{
s_ProgramInitializationSection.Leave();
}
OnProgramInitializationComplete.Raise(null);
}
}
}

View File

@@ -138,42 +138,6 @@ namespace ICD.Common.Utils
return (ushort)MapRange((double)inputStart, inputEnd, outputStart, outputEnd, value);
}
/// <summary>
/// Returns the value after the input range has been mapped to a new range
/// </summary>
/// <param name="inputStart">Input start.</param>
/// <param name="inputEnd">Input end.</param>
/// <param name="outputStart">Output start.</param>
/// <param name="outputEnd">Output end.</param>
/// <param name="value">Value.</param>
/// <returns>The newly mapped value</returns>
public static ulong MapRange(ulong inputStart, ulong inputEnd, ulong outputStart, ulong outputEnd, ulong value)
{
if (inputStart.Equals(inputEnd))
throw new DivideByZeroException();
ulong slope = (outputEnd - outputStart) / (inputEnd - inputStart);
return outputStart + slope * (value - inputStart);
}
/// <summary>
/// Returns the value after the input range has been mapped to a new range
/// </summary>
/// <param name="inputStart">Input start.</param>
/// <param name="inputEnd">Input end.</param>
/// <param name="outputStart">Output start.</param>
/// <param name="outputEnd">Output end.</param>
/// <param name="value">Value.</param>
/// <returns>The newly mapped value</returns>
public static long MapRange(long inputStart, long inputEnd, long outputStart, long outputEnd, long value)
{
if (inputStart.Equals(inputEnd))
throw new DivideByZeroException();
long slope = (outputEnd - outputStart) / (inputEnd - inputStart);
return outputStart + slope * (value - inputStart);
}
/// <summary>
/// Maps the date in the given range to the float range 0.0f to 1.0f.
/// 0.5f - The date is half way between the end points.

View File

@@ -4,4 +4,4 @@ using System.Reflection;
[assembly: AssemblyCompany("ICD Systems")]
[assembly: AssemblyProduct("ICD.Common.Utils")]
[assembly: AssemblyCopyright("Copyright © ICD Systems 2019")]
[assembly: AssemblyVersion("8.4.1.0")]
[assembly: AssemblyVersion("8.7.2.0")]

View File

@@ -67,8 +67,8 @@ namespace ICD.Common.Utils.Timers
/// <returns></returns>
public static SafeTimer Stopped(Action callback)
{
// Some arbitrarily large number that shouldn't timeout before we call stop.
SafeTimer output = new SafeTimer(callback, 100 * 1000, 100 * 1000);
//No due time or repeat period on a stopped timer
SafeTimer output = new SafeTimer(callback, -1, -1);
output.Stop();
return output;
}

View File

@@ -9,8 +9,12 @@ namespace ICD.Common.Utils.Xml
{
public sealed class IcdXmlException : Exception
{
private int m_LineNumber;
private int m_LinePosition;
private readonly int m_LineNumber;
private readonly int m_LinePosition;
public int LineNumber { get { return m_LineNumber; } }
public int LinePosition { get { return m_LinePosition; } }
/// <summary>
/// Constructor.