Make further properties multi-client and thread-safe

Move MovingPrimary, MovingSecondary, EarliestNonSlewingTime to
SharedResources, make all new properties thread-safe (atomic)
operations.
This commit is contained in:
Sebastian Godelet
2021-06-22 17:21:21 +10:00
parent af750549fe
commit 6fc476b031
16 changed files with 435 additions and 44 deletions
@@ -1004,20 +1004,28 @@ namespace Meade.net.Telescope.UnitTests
_sharedResourcesWrapperMock.Verify(x => x.SendChar("P", false), Times.AtLeastOnce); _sharedResourcesWrapperMock.Verify(x => x.SendChar("P", false), Times.AtLeastOnce);
} }
[TestCase("High", false, true)] [TestCase("High")]
[TestCase("High", true, true)] [TestCase("Low")]
[TestCase("Low", false, false)] public void Precision_Set_WhenSecondConnectionMade_ThenTelescopePrecisionNotChanged(string desiredPresision)
[TestCase("Low", true, false)]
public void Precision_Set_WhenSecondConnectionMade_ThenTelescopePrecisionNotChanged(string desiredPresision, bool telescopePrecision, bool finalPrecision)
{ {
var isLongFormat = desiredPresision == "High"
|| (desiredPresision == "Low"
? false
: throw new ArgumentOutOfRangeException(nameof(desiredPresision), desiredPresision, "Should be High or Low"));
_sharedResourcesWrapperMock.SetupProperty(x => x.IsLongFormat, isLongFormat);
_sharedResourcesWrapperMock.Setup(x => x.SendString("GR", false)).Returns(() => _testProperties.telescopeRaResult);
_utilMock.Setup(x => x.HMSToHours(_testProperties.telescopeRaResult)).Returns(() => _testProperties.rightAscension);
_profileProperties.Precision = desiredPresision; _profileProperties.Precision = desiredPresision;
_connectionInfo.SameDevice = 2; _connectionInfo.SameDevice = 2;
//_connectionInfo.Connections = 2; //_connectionInfo.Connections = 2;
_telescope.Connected = true; _telescope.Connected = true;
_sharedResourcesWrapperMock.Verify(x => x.SendChar("P", false), Times.Never); _sharedResourcesWrapperMock.Verify(x => x.SendChar("P", false), Times.Never);
_sharedResourcesWrapperMock.Verify(x => x.IsLongFormat, Times.Once);
} }
[Test] [Test]
@@ -1687,6 +1695,9 @@ namespace Meade.net.Telescope.UnitTests
[TestCase(GuideDirections.guideSouth, TelescopeAxes.axisSecondary)] [TestCase(GuideDirections.guideSouth, TelescopeAxes.axisSecondary)]
public void PulseGuide_WhenMovingAxisAndPulseGuideAttempted_ThenThrowsExpectedException(GuideDirections direction, TelescopeAxes axes) public void PulseGuide_WhenMovingAxisAndPulseGuideAttempted_ThenThrowsExpectedException(GuideDirections direction, TelescopeAxes axes)
{ {
_sharedResourcesWrapperMock.SetupProperty(x => x.MovingPrimary);
_sharedResourcesWrapperMock.SetupProperty(x => x.MovingSecondary);
_sharedResourcesWrapperMock.SetupProperty(x => x.EarliestNonSlewingTime, DateTime.MinValue);
_sharedResourcesWrapperMock.Setup(x => x.SendString("D", false)).Returns(""); _sharedResourcesWrapperMock.Setup(x => x.SendString("D", false)).Returns("");
var duration = 0; var duration = 0;
@@ -1697,6 +1708,8 @@ namespace Meade.net.Telescope.UnitTests
var exception = Assert.Throws<InvalidOperationException>(() => _telescope.PulseGuide(direction, duration)); var exception = Assert.Throws<InvalidOperationException>(() => _telescope.PulseGuide(direction, duration));
Assert.That(exception.Message, Is.EqualTo("Unable to PulseGuide while moving same axis.")); Assert.That(exception.Message, Is.EqualTo("Unable to PulseGuide while moving same axis."));
Assert.That(_sharedResourcesWrapperMock.Object.MovingPrimary, Is.EqualTo(axes == TelescopeAxes.axisPrimary));
Assert.That(_sharedResourcesWrapperMock.Object.MovingSecondary, Is.EqualTo(axes == TelescopeAxes.axisSecondary));
} }
[TestCase(GuideDirections.guideEast)] [TestCase(GuideDirections.guideEast)]
@@ -2909,6 +2922,7 @@ namespace Meade.net.Telescope.UnitTests
[TestCase(15, 10, "2021-10-03T20:36:00", "2021-10-03T20:36:25", false)] [TestCase(15, 10, "2021-10-03T20:36:00", "2021-10-03T20:36:25", false)]
public void Slewing_WhenTelescopeIsSlewing_ThenReturnsExpectedValueForSettleTime(short settleTime, short profileSettleTime, string startSlewing, string endSlewing, bool isSlewing) public void Slewing_WhenTelescopeIsSlewing_ThenReturnsExpectedValueForSettleTime(short settleTime, short profileSettleTime, string startSlewing, string endSlewing, bool isSlewing)
{ {
_sharedResourcesWrapperMock.SetupProperty(x => x.EarliestNonSlewingTime, DateTime.MinValue);
_profileProperties.SettleTime = profileSettleTime; _profileProperties.SettleTime = profileSettleTime;
var timescalled = 0; var timescalled = 0;
@@ -2979,6 +2993,10 @@ namespace Meade.net.Telescope.UnitTests
[TestCase(-1, TelescopeAxes.axisSecondary)] [TestCase(-1, TelescopeAxes.axisSecondary)]
public void Slewing_WhenTelescopeIsMoving_ThenDoesNotSendCommandAndReturnsTrue(int rate, TelescopeAxes axis) public void Slewing_WhenTelescopeIsMoving_ThenDoesNotSendCommandAndReturnsTrue(int rate, TelescopeAxes axis)
{ {
_sharedResourcesWrapperMock.SetupProperty(x => x.MovingPrimary);
_sharedResourcesWrapperMock.SetupProperty(x => x.MovingSecondary);
_sharedResourcesWrapperMock.SetupProperty(x => x.EarliestNonSlewingTime, DateTime.MinValue);
ConnectTelescope(); ConnectTelescope();
_telescope.MoveAxis(axis, rate); _telescope.MoveAxis(axis, rate);
@@ -2986,6 +3004,9 @@ namespace Meade.net.Telescope.UnitTests
var result = _telescope.Slewing; var result = _telescope.Slewing;
Assert.That(result, Is.True); Assert.That(result, Is.True);
Assert.That(_sharedResourcesWrapperMock.Object.MovingPrimary, Is.EqualTo(axis == TelescopeAxes.axisPrimary));
Assert.That(_sharedResourcesWrapperMock.Object.MovingSecondary, Is.EqualTo(axis == TelescopeAxes.axisSecondary));
_sharedResourcesWrapperMock.Verify(x => x.SendString("D", false), Times.Never); _sharedResourcesWrapperMock.Verify(x => x.SendString("D", false), Times.Never);
} }
@@ -3005,6 +3026,11 @@ namespace Meade.net.Telescope.UnitTests
[TestCase(-1, TelescopeAxes.axisSecondary, 10, 20, true, true)] [TestCase(-1, TelescopeAxes.axisSecondary, 10, 20, true, true)]
public void Slewing_WhenTelescopeStops_ThenWaitsForSettleTime(int rate, TelescopeAxes axis, short profileSettleTime, short driverSettleTime, bool expectedResultInWaitingPeriod, bool afterProfileSettleTimeUp) public void Slewing_WhenTelescopeStops_ThenWaitsForSettleTime(int rate, TelescopeAxes axis, short profileSettleTime, short driverSettleTime, bool expectedResultInWaitingPeriod, bool afterProfileSettleTimeUp)
{ {
_sharedResourcesWrapperMock.SetupProperty(x => x.MovingPrimary);
_sharedResourcesWrapperMock.SetupProperty(x => x.MovingSecondary);
_sharedResourcesWrapperMock.SetupProperty(x => x.SlewSettleTime);
_sharedResourcesWrapperMock.SetupProperty(x => x.EarliestNonSlewingTime, DateTime.MinValue);
_profileProperties.SettleTime = profileSettleTime; _profileProperties.SettleTime = profileSettleTime;
DateTime currentTime = MakeTime("2021-01-23T22:02:10"); DateTime currentTime = MakeTime("2021-01-23T22:02:10");
@@ -3022,17 +3048,17 @@ namespace Meade.net.Telescope.UnitTests
_telescope.MoveAxis(axis, 0); _telescope.MoveAxis(axis, 0);
currentTime = currentTime + TimeSpan.FromSeconds(profileSettleTime / 2); currentTime += TimeSpan.FromSeconds(profileSettleTime / 2);
result = _telescope.Slewing; result = _telescope.Slewing;
Assert.That(result, Is.EqualTo(expectedResultInWaitingPeriod)); Assert.That(result, Is.EqualTo(expectedResultInWaitingPeriod));
currentTime = currentTime + TimeSpan.FromSeconds(profileSettleTime / 2); currentTime += TimeSpan.FromSeconds(profileSettleTime / 2);
result = _telescope.Slewing; result = _telescope.Slewing;
Assert.That(result, Is.EqualTo(afterProfileSettleTimeUp)); Assert.That(result, Is.EqualTo(afterProfileSettleTimeUp));
currentTime = currentTime + TimeSpan.FromSeconds(driverSettleTime); currentTime += TimeSpan.FromSeconds(driverSettleTime);
result = _telescope.Slewing; result = _telescope.Slewing;
Assert.That(result, Is.False); Assert.That(result, Is.False);
+13 -22
View File
@@ -938,8 +938,8 @@ namespace ASCOM.Meade.net
//:Q# Halt all current slewing //:Q# Halt all current slewing
//Returns:Nothing //Returns:Nothing
_movingPrimary = false; SharedResourcesWrapper.MovingPrimary = false;
_movingSecondary = false; SharedResourcesWrapper.MovingSecondary = false;
SetSlewingMinEndTime(); SetSlewingMinEndTime();
} }
@@ -1522,9 +1522,6 @@ namespace ASCOM.Meade.net
} }
} }
private bool _movingPrimary;
private bool _movingSecondary;
public void MoveAxis(TelescopeAxes axis, double rate) public void MoveAxis(TelescopeAxes axis, double rate)
{ {
LogMessage("MoveAxis", $"Axis={axis} rate={rate}"); LogMessage("MoveAxis", $"Axis={axis} rate={rate}");
@@ -1573,7 +1570,7 @@ namespace ASCOM.Meade.net
SetSlewingMinEndTime(); SetSlewingMinEndTime();
} }
_movingPrimary = false; SharedResourcesWrapper.MovingPrimary = false;
SharedResourcesWrapper.SendBlind("Qe"); SharedResourcesWrapper.SendBlind("Qe");
//:Qe# Halt eastward Slews //:Qe# Halt eastward Slews
//Returns: Nothing //Returns: Nothing
@@ -1585,17 +1582,13 @@ namespace ASCOM.Meade.net
SharedResourcesWrapper.SendBlind("Me"); SharedResourcesWrapper.SendBlind("Me");
//:Me# Move Telescope East at current slew rate //:Me# Move Telescope East at current slew rate
//Returns: Nothing //Returns: Nothing
_movingPrimary = true; SharedResourcesWrapper.MovingPrimary = true;
// in principle we could calculate the current side of pier, but unknown is the safer option.
SharedResourcesWrapper.SideOfPier = PierSide.pierUnknown;
break; break;
case ComparisonResult.Lower: case ComparisonResult.Lower:
SharedResourcesWrapper.SendBlind("Mw"); SharedResourcesWrapper.SendBlind("Mw");
//:Mw# Move Telescope West at current slew rate //:Mw# Move Telescope West at current slew rate
//Returns: Nothing //Returns: Nothing
_movingPrimary = true; SharedResourcesWrapper.MovingPrimary = true;
// in principle we could calculate the current side of pier, but unknown is the safer option.
SharedResourcesWrapper.SideOfPier = PierSide.pierUnknown;
break; break;
} }
break; break;
@@ -1607,7 +1600,7 @@ namespace ASCOM.Meade.net
{ {
SetSlewingMinEndTime(); SetSlewingMinEndTime();
} }
_movingSecondary = false; SharedResourcesWrapper.MovingSecondary = false;
SharedResourcesWrapper.SendBlind("Qn"); SharedResourcesWrapper.SendBlind("Qn");
//:Qn# Halt northward Slews //:Qn# Halt northward Slews
//Returns: Nothing //Returns: Nothing
@@ -1619,13 +1612,13 @@ namespace ASCOM.Meade.net
SharedResourcesWrapper.SendBlind("Mn"); SharedResourcesWrapper.SendBlind("Mn");
//:Mn# Move Telescope North at current slew rate //:Mn# Move Telescope North at current slew rate
//Returns: Nothing //Returns: Nothing
_movingSecondary = true; SharedResourcesWrapper.MovingSecondary = true;
break; break;
case ComparisonResult.Lower: case ComparisonResult.Lower:
SharedResourcesWrapper.SendBlind("Ms"); SharedResourcesWrapper.SendBlind("Ms");
//:Ms# Move Telescope South at current slew rate //:Ms# Move Telescope South at current slew rate
//Returns: Nothing //Returns: Nothing
_movingSecondary = true; SharedResourcesWrapper.MovingSecondary = true;
break; break;
} }
break; break;
@@ -1695,11 +1688,11 @@ namespace ASCOM.Meade.net
_isGuiding = true; _isGuiding = true;
try try
{ {
if (_movingPrimary && if (SharedResourcesWrapper.MovingPrimary &&
(direction == GuideDirections.guideEast || direction == GuideDirections.guideWest)) (direction == GuideDirections.guideEast || direction == GuideDirections.guideWest))
throw new InvalidOperationException("Unable to PulseGuide while moving same axis."); throw new InvalidOperationException("Unable to PulseGuide while moving same axis.");
if (_movingSecondary && if (SharedResourcesWrapper.MovingSecondary &&
(direction == GuideDirections.guideNorth || direction == GuideDirections.guideSouth)) (direction == GuideDirections.guideNorth || direction == GuideDirections.guideSouth))
throw new InvalidOperationException("Unable to PulseGuide while moving same axis."); throw new InvalidOperationException("Unable to PulseGuide while moving same axis.");
@@ -2279,11 +2272,9 @@ namespace ASCOM.Meade.net
if (_isGuiding) if (_isGuiding)
return false; return false;
return _movingPrimary || _movingSecondary; return SharedResourcesWrapper.MovingPrimary || SharedResourcesWrapper.MovingSecondary;
} }
private DateTime _earliestNonSlewingTime = DateTime.MinValue;
public bool Slewing public bool Slewing
{ {
get get
@@ -2292,7 +2283,7 @@ namespace ASCOM.Meade.net
if (isSlewing) if (isSlewing)
SetSlewingMinEndTime(); SetSlewingMinEndTime();
else if (_clock.UtcNow < _earliestNonSlewingTime) else if (_clock.UtcNow < SharedResourcesWrapper.EarliestNonSlewingTime)
isSlewing = true; isSlewing = true;
LogMessage("Slewing", $"Result = {isSlewing}"); LogMessage("Slewing", $"Result = {isSlewing}");
@@ -2302,7 +2293,7 @@ namespace ASCOM.Meade.net
private void SetSlewingMinEndTime() private void SetSlewingMinEndTime()
{ {
_earliestNonSlewingTime = _clock.UtcNow + GetTotalSlewingSettleTime(); SharedResourcesWrapper.EarliestNonSlewingTime = _clock.UtcNow + GetTotalSlewingSettleTime();
} }
private TimeSpan GetTotalSlewingSettleTime() private TimeSpan GetTotalSlewingSettleTime()
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\NUnit3TestAdapter.3.13.0\build\net35\NUnit3TestAdapter.props" Condition="Exists('..\packages\NUnit3TestAdapter.3.13.0\build\net35\NUnit3TestAdapter.props')" />
<Import Project="..\packages\NUnit.3.13.0\build\NUnit.props" Condition="Exists('..\packages\NUnit.3.13.0\build\NUnit.props')" /> <Import Project="..\packages\NUnit.3.13.0\build\NUnit.props" Condition="Exists('..\packages\NUnit.3.13.0\build\NUnit.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
@@ -98,6 +99,10 @@
<ItemGroup> <ItemGroup>
<Compile Include="SharedResourcesUnitTests.cs" /> <Compile Include="SharedResourcesUnitTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ThreadSafeBoolTests.cs" />
<Compile Include="ThreadSafeDateTimeTests.cs" />
<Compile Include="ThreadSafeEnumTests.cs" />
<Compile Include="ThreadSafeNullableDoubleTests.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Meade.net\Meade.net.csproj"> <ProjectReference Include="..\Meade.net\Meade.net.csproj">
@@ -116,5 +121,6 @@
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup> </PropertyGroup>
<Error Condition="!Exists('..\packages\NUnit.3.13.0\build\NUnit.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\NUnit.3.13.0\build\NUnit.props'))" /> <Error Condition="!Exists('..\packages\NUnit.3.13.0\build\NUnit.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\NUnit.3.13.0\build\NUnit.props'))" />
<Error Condition="!Exists('..\packages\NUnit3TestAdapter.3.13.0\build\net35\NUnit3TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\NUnit3TestAdapter.3.13.0\build\net35\NUnit3TestAdapter.props'))" />
</Target> </Target>
</Project> </Project>
@@ -29,7 +29,7 @@ namespace Meade.net.UnitTests
[Test] [Test]
public void CheckThatSerialPortIsSetToUseMock() public void CheckThatSerialPortIsSetToUseMock()
{ {
Assert.That(SharedResources.SharedSerial,Is.EqualTo(_serialMock.Object)); Assert.That(SharedResources.SharedSerial, Is.EqualTo(_serialMock.Object));
} }
[TestCase(true, "Test")] [TestCase(true, "Test")]
@@ -160,7 +160,7 @@ namespace Meade.net.UnitTests
string GuideRateProfileNameDefault = "10.077939"; //67% of sidereal rate string GuideRateProfileNameDefault = "10.077939"; //67% of sidereal rate
string PrecisionDefault = "Unchanged"; string PrecisionDefault = "Unchanged";
string GuidingStyleDefault = "Auto"; string GuidingStyleDefault = "Auto";
string BacklashCompensationDefault = "3000"; string BacklashCompensationDefault = "3000";
string ReverseFocuserDiectionDefault = "true"; string ReverseFocuserDiectionDefault = "true";
@@ -239,15 +239,15 @@ namespace Meade.net.UnitTests
SharedResources.ProfileFactory = profileFactoryMock.Object; SharedResources.ProfileFactory = profileFactoryMock.Object;
var profileProperties = SharedResources.ReadProfile(); var profileProperties = SharedResources.ReadProfile();
Assert.That(profeWrapper.DeviceType, Is.EqualTo("Telescope")); Assert.That(profeWrapper.DeviceType, Is.EqualTo("Telescope"));
Assert.That(profileProperties.TraceLogger, Is.EqualTo(bool.Parse(TraceStateDefault))); Assert.That(profileProperties.TraceLogger, Is.EqualTo(bool.Parse(TraceStateDefault)));
Assert.That(profileProperties.ComPort, Is.EqualTo(ComPortDefault)); Assert.That(profileProperties.ComPort, Is.EqualTo(ComPortDefault));
Assert.That(profileProperties.GuideRateArcSecondsPerSecond, Assert.That(profileProperties.GuideRateArcSecondsPerSecond,
Is.EqualTo(double.Parse(GuideRateProfileNameDefault))); Is.EqualTo(double.Parse(GuideRateProfileNameDefault)));
Assert.That(profileProperties.Precision, Is.EqualTo(PrecisionDefault)); Assert.That(profileProperties.Precision, Is.EqualTo(PrecisionDefault));
Assert.That(profileProperties.GuidingStyle, Is.EqualTo(GuidingStyleDefault)); Assert.That(profileProperties.GuidingStyle, Is.EqualTo(GuidingStyleDefault));
@@ -423,7 +423,7 @@ namespace Meade.net.UnitTests
string serialPortReturn = string.Empty; string serialPortReturn = string.Empty;
_serialMock.Setup(x => x.Transmit("#:GVP#")).Callback(() => { _serialMock.Setup(x => x.Transmit("#:GVP#")).Callback(() => {
serialPortReturn = string.Empty; serialPortReturn = string.Empty;
throw new Exception("Testerror"); throw new Exception("Testerror");
}); });
@@ -0,0 +1,39 @@
using ASCOM.Meade.net;
using NUnit.Framework;
namespace Meade.net.UnitTests
{
public class ThreadSafeBoolTests
{
[TestCase(false)]
[TestCase(true)]
public void WhenConvertedValueIsSame(bool value)
{
// given
ThreadSafeBool sut = value;
// when
bool actual = sut;
// then
Assert.That(actual, Is.EqualTo(value));
}
[TestCase(false, false)]
[TestCase(false, true)]
[TestCase(true, false)]
[TestCase(true, true)]
public void WhenSetValueIsChanged(bool value, bool setValue)
{
// given
ThreadSafeBool sut = value;
// when
sut.Set(setValue);
bool afterset = sut;
// then
Assert.That(afterset, Is.EqualTo(setValue));
}
}
}
@@ -0,0 +1,64 @@
using ASCOM.Meade.net;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Meade.net.UnitTests
{
public class ThreadSafeDateTimeTests
{
[TestCaseSource(nameof(DateTimeSource))]
public void WhenConvertedValueIsSame(DateTime value)
{
// given
ThreadSafeDateTime sut = value;
// when
DateTime actual = sut;
// then
Assert.That(actual, Is.EqualTo(value));
}
[TestCaseSource(nameof(DateTimeSetSource))]
public void WhenSetValueIsChanged(DateTime value, DateTime setValue)
{
// given
ThreadSafeDateTime sut = value;
// when
sut.Set(setValue);
DateTime afterset = sut;
// then
Assert.That(afterset, Is.EqualTo(setValue));
}
static readonly DateTime Example1 = DateTimeOffset.Parse("2012-05-09T02:10:31.296761Z", CultureInfo.InvariantCulture).UtcDateTime;
static readonly DateTime Example2 = DateTimeOffset.Parse("2051-03-09T23:15:11.556081Z", CultureInfo.InvariantCulture).UtcDateTime;
static IEnumerable<DateTime> DateTimeSource => new[]
{
DateTime.MinValue,
Example1,
Example2
};
static IEnumerable<TestCaseData> DateTimeSetSource => new[]
{
new TestCaseData(DateTime.MinValue, Example1),
new TestCaseData(DateTime.MinValue, Example2),
new TestCaseData(DateTime.MinValue, DateTime.MinValue),
new TestCaseData(Example1, Example1),
new TestCaseData(Example1, Example2),
new TestCaseData(Example1, DateTime.MinValue),
new TestCaseData(Example2, Example1),
new TestCaseData(Example2, Example2),
new TestCaseData(Example2, DateTime.MinValue)
};
}
}
@@ -0,0 +1,51 @@
using ASCOM.DeviceInterface;
using ASCOM.Meade.net;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Meade.net.UnitTests
{
public class ThreadSafeEnumTests
{
[TestCase(PierSide.pierUnknown)]
[TestCase(PierSide.pierEast)]
[TestCase(PierSide.pierWest)]
public void WhenConvertedValueIsSame(PierSide value)
{
// given
ThreadSafeEnum<PierSide> sut = value;
// when
PierSide actual = sut;
// then
Assert.That(actual, Is.EqualTo(value));
}
[TestCase(PierSide.pierUnknown, PierSide.pierUnknown)]
[TestCase(PierSide.pierUnknown, PierSide.pierUnknown)]
[TestCase(PierSide.pierUnknown, PierSide.pierUnknown)]
[TestCase(PierSide.pierEast, PierSide.pierUnknown)]
[TestCase(PierSide.pierEast, PierSide.pierWest)]
[TestCase(PierSide.pierEast, PierSide.pierEast)]
[TestCase(PierSide.pierWest, PierSide.pierUnknown)]
[TestCase(PierSide.pierWest, PierSide.pierWest)]
[TestCase(PierSide.pierWest, PierSide.pierEast)]
public void WhenSetValueIsChanged(PierSide value, PierSide setValue)
{
// given
ThreadSafeEnum<PierSide> sut = value;
// when
sut.Set(setValue);
PierSide afterset = sut;
// then
Assert.That(afterset, Is.EqualTo(setValue));
}
}
}
@@ -0,0 +1,45 @@
using ASCOM.Meade.net;
using NUnit.Framework;
namespace Meade.net.UnitTests
{
public class ThreadSafeNullableDoubleTests
{
[TestCase(0.1d)]
[TestCase(-12.34d)]
[TestCase(0d)]
[TestCase(null)]
public void WhenConvertedValueIsSame(double? value)
{
// given
ThreadSafeNullableDouble sut = value;
// when
double? actual = sut;
// then
Assert.That(actual, Is.EqualTo(value));
}
[TestCase(0.1d, 0.2d)]
[TestCase(-12.34d, 5d)]
[TestCase(0d, 1d)]
[TestCase(null, 2d)]
[TestCase(0.1d, null)]
[TestCase(-12.34d, null)]
[TestCase(0d, null)]
[TestCase(null, null)]
public void WhenSetValueIsChanged(double? value, double? setValue)
{
// given
ThreadSafeNullableDouble sut = value;
// when
sut.Set(setValue);
double? afterset = sut;
// then
Assert.That(afterset, Is.EqualTo(setValue));
}
}
}
+2
View File
@@ -5,6 +5,8 @@
<package id="JetBrains.Annotations" version="2020.3.0" targetFramework="net472" /> <package id="JetBrains.Annotations" version="2020.3.0" targetFramework="net472" />
<package id="Moq" version="4.15.2" targetFramework="net472" /> <package id="Moq" version="4.15.2" targetFramework="net472" />
<package id="NUnit" version="3.13.0" targetFramework="net472" /> <package id="NUnit" version="3.13.0" targetFramework="net472" />
<package id="NUnit.ConsoleRunner" version="3.12.0" targetFramework="net472" />
<package id="NUnit3TestAdapter" version="3.13.0" targetFramework="net472" />
<package id="System.Runtime.CompilerServices.Unsafe" version="5.0.0" targetFramework="net472" /> <package id="System.Runtime.CompilerServices.Unsafe" version="5.0.0" targetFramework="net472" />
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net472" /> <package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net472" />
</packages> </packages>
+4
View File
@@ -146,6 +146,10 @@
<Compile Include="ProfileProperties.cs" /> <Compile Include="ProfileProperties.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TelescopeList.cs" /> <Compile Include="TelescopeList.cs" />
<Compile Include="ThreadSafeBool.cs" />
<Compile Include="ThreadSafeDateTime.cs" />
<Compile Include="ThreadSafeEnum.cs" />
<Compile Include="ThreadSafeNullableDouble.cs" />
<Compile Include="Win32Utilities.cs" /> <Compile Include="Win32Utilities.cs" />
<Compile Include="Wrapper\IProfileWrapper.cs" /> <Compile Include="Wrapper\IProfileWrapper.cs" />
<Compile Include="Wrapper\SharedResourcesWrapper.cs" /> <Compile Include="Wrapper\SharedResourcesWrapper.cs" />
+65 -8
View File
@@ -19,6 +19,7 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.AccessControl; using System.Security.AccessControl;
using System.Threading;
using System.Windows.Forms; using System.Windows.Forms;
using ASCOM.DeviceInterface; using ASCOM.DeviceInterface;
using ASCOM.Meade.net.Wrapper; using ASCOM.Meade.net.Wrapper;
@@ -245,7 +246,7 @@ namespace ASCOM.Meade.net
private const string HandShakeDefault = "None"; private const string HandShakeDefault = "None";
private const string ParityDefault = "None"; private const string ParityDefault = "None";
private const string SendDateTimeDefault = "false"; private const string SendDateTimeDefault = "false";
private static string ParkedBehaviourDefault = "No Coordinates"; private const string ParkedBehaviourDefault = "No Coordinates";
private const string ParkedAltDefault = "0"; private const string ParkedAltDefault = "0";
private const string ParkedAzimuthDefault = "180"; private const string ParkedAzimuthDefault = "180";
@@ -501,22 +502,78 @@ namespace ASCOM.Meade.net
ParkedPosition = parkedPosition; ParkedPosition = parkedPosition;
} }
public static bool IsParked { get; private set; } private static readonly ThreadSafeBool _isParked = false;
public static bool IsParked
{
get => _isParked;
private set => _isParked.Set(value);
}
public static ParkedPosition ParkedPosition { get; private set; } private static ParkedPosition _parkedPosition;
public static ParkedPosition ParkedPosition
{
get => _parkedPosition;
private set => Interlocked.Exchange(ref _parkedPosition, value);
}
private static readonly ThreadSafeEnum<PierSide> _sideOfPier = PierSide.pierUnknown;
/// <summary> /// <summary>
/// Start with <see cref="PierSide.pierUnknown"/>. /// Start with <see cref="PierSide.pierUnknown"/>.
/// As we do not know the physical declination axis position, we have to keep track manually. /// As we do not know the physical declination axis position, we have to keep track manually.
/// </summary> /// </summary>
public static PierSide SideOfPier { get; internal set; } = PierSide.pierUnknown; public static PierSide SideOfPier
{
get => _sideOfPier;
internal set => _sideOfPier.Set(value);
}
public static double? TargetRightAscension { get; internal set; } private static readonly ThreadSafeNullableDouble _targetRightAscension = null as double?;
public static double? TargetRightAscension
{
get => _targetRightAscension;
internal set => _targetRightAscension.Set(value);
}
public static double? TargetDeclination { get; internal set; } private static readonly ThreadSafeNullableDouble _targetDeclination = null as double?;
public static double? TargetDeclination
{
get => _targetDeclination;
internal set => _targetDeclination.Set(value);
}
public static short SlewSettleTime { get; internal set; } private static int _slewSettleTime;
public static short SlewSettleTime
{
get => Convert.ToInt16(_slewSettleTime);
internal set => Interlocked.Exchange(ref _slewSettleTime, value);
}
public static bool IsLongFormat { get; internal set; } private static readonly ThreadSafeBool _isLongFormat = false;
public static bool IsLongFormat
{
get => _isLongFormat;
internal set => _isLongFormat.Set(value);
}
private static readonly ThreadSafeBool _movingPrimary = false;
public static bool MovingPrimary
{
get => _movingPrimary;
internal set => _movingPrimary.Set(value);
}
private static readonly ThreadSafeBool _movingSecondary = false;
public static bool MovingSecondary
{
get => _movingSecondary;
internal set => _movingSecondary.Set(value);
}
private static readonly ThreadSafeDateTime _earliestNonSlewingTime = DateTime.MinValue;
public static DateTime EarliestNonSlewingTime
{
get => _earliestNonSlewingTime;
internal set => _earliestNonSlewingTime.Set(value);
}
} }
} }
+17
View File
@@ -0,0 +1,17 @@
using System.Threading;
namespace ASCOM.Meade.net
{
public class ThreadSafeBool
{
private object _value;
public ThreadSafeBool(in bool value) => _value = value;
public void Set(in bool value) => Interlocked.Exchange(ref _value, value);
public static implicit operator ThreadSafeBool(in bool value) => new ThreadSafeBool(value);
public static implicit operator bool(ThreadSafeBool @this) => (bool)@this._value;
}
}
+20
View File
@@ -0,0 +1,20 @@
using System;
using System.Threading;
namespace ASCOM.Meade.net
{
public class ThreadSafeDateTime
{
private long _value;
public ThreadSafeDateTime(in DateTime value) => _value = DateTimeToLong(value);
public void Set(in DateTime value) => Interlocked.Exchange(ref _value, DateTimeToLong(value));
private static long DateTimeToLong(in DateTime value) => value.ToUniversalTime().Ticks;
public static implicit operator ThreadSafeDateTime(in DateTime value) => new ThreadSafeDateTime(value);
public static implicit operator DateTime(ThreadSafeDateTime @this) => new DateTime(Interlocked.Read(ref @this._value), DateTimeKind.Utc);
}
}
+21
View File
@@ -0,0 +1,21 @@
using System;
using System.Threading;
namespace ASCOM.Meade.net
{
public class ThreadSafeEnum<T>
where T: struct, Enum
{
private long _value;
public ThreadSafeEnum(T value) => _value = EnumToLong(value);
public void Set(T value) => Interlocked.Exchange(ref _value, EnumToLong(value));
private static long EnumToLong(T value) => Convert.ToInt64(value);
public static implicit operator ThreadSafeEnum<T>(T value) => new ThreadSafeEnum<T>(value);
public static implicit operator T(ThreadSafeEnum<T> @this) => (T) Enum.ToObject(typeof(T), Interlocked.Read(ref @this._value));
}
}
+24
View File
@@ -0,0 +1,24 @@
using System;
using System.Threading;
namespace ASCOM.Meade.net
{
public class ThreadSafeNullableDouble
{
private long _value;
public ThreadSafeNullableDouble(in double? value) => _value = NullableDoubleToLong(value);
public void Set(in double? value) => Interlocked.Exchange(ref _value, NullableDoubleToLong(value));
private static long NullableDoubleToLong(in double? value) => BitConverter.DoubleToInt64Bits(value ?? double.NaN);
public static implicit operator ThreadSafeNullableDouble(in double? value) => new ThreadSafeNullableDouble(value);
public static implicit operator double?(ThreadSafeNullableDouble @this)
{
var doubleValue = BitConverter.Int64BitsToDouble(Interlocked.Read(ref @this._value));
return double.IsNaN(doubleValue) ? null as double? : doubleValue;
}
}
}
@@ -40,6 +40,12 @@ namespace ASCOM.Meade.net.Wrapper
short SlewSettleTime { get; set; } short SlewSettleTime { get; set; }
bool IsLongFormat { get; set; } bool IsLongFormat { get; set; }
bool MovingPrimary { get; set; }
bool MovingSecondary { get; set; }
DateTime EarliestNonSlewingTime { get; set; }
} }
public class SharedResourcesWrapper : ISharedResourcesWrapper public class SharedResourcesWrapper : ISharedResourcesWrapper
@@ -151,5 +157,23 @@ namespace ASCOM.Meade.net.Wrapper
get => SharedResources.IsLongFormat; get => SharedResources.IsLongFormat;
set => SharedResources.IsLongFormat = value; set => SharedResources.IsLongFormat = value;
} }
public bool MovingPrimary
{
get => SharedResources.MovingPrimary;
set => SharedResources.MovingPrimary = value;
}
public bool MovingSecondary
{
get => SharedResources.MovingSecondary;
set => SharedResources.MovingSecondary = value;
}
public DateTime EarliestNonSlewingTime
{
get => SharedResources.EarliestNonSlewingTime;
set => SharedResources.EarliestNonSlewingTime = value;
}
} }
} }