Switched the serial port over to using the .net frameworks serial port.
Extracted the serial port into it's own class and created a simpler command processing mechanism.
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Ports;
|
||||
using ASCOM;
|
||||
using ASCOM.MeadeAutostar497.Controller;
|
||||
using ASCOM.Utilities;
|
||||
using ASCOM.Utilities.Interfaces;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
|
||||
@@ -11,26 +11,25 @@ namespace MeadeAutostar497.UnitTests
|
||||
[TestFixture]
|
||||
public class TelescopeControllerUnitTests
|
||||
{
|
||||
private Mock<ISerial> serialMock;
|
||||
private Mock<ISerialProcessor> serialMock;
|
||||
|
||||
private readonly List<string> _availableComPorts = new List<string> { "COM1", "COM2", "COM3" };
|
||||
private TelescopeController _telescopeController;
|
||||
|
||||
private string transmittedString;
|
||||
private string stringToRecieve;
|
||||
private string _stringToRecieve = string.Empty;
|
||||
private bool _isConnected = false;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
transmittedString = string.Empty;
|
||||
stringToRecieve = string.Empty;
|
||||
_stringToRecieve = string.Empty;
|
||||
_isConnected = false;
|
||||
|
||||
serialMock = new Mock<ISerial>();
|
||||
serialMock = new Mock<ISerialProcessor>();
|
||||
serialMock.SetupAllProperties();
|
||||
|
||||
serialMock.Setup(x => x.AvailableComPorts).Returns( () => _availableComPorts.ToArray());
|
||||
serialMock.Setup(X => X.Transmit(It.IsAny<string>())).Callback<string>(str => { transmittedString = str; });
|
||||
serialMock.Setup(X => X.Receive()).Returns(() => stringToRecieve);
|
||||
serialMock.Setup(x => x.GetPortNames()).Returns( () => _availableComPorts.ToArray());
|
||||
serialMock.Setup(x => x.CommandTerminated(It.IsAny<string>(), It.IsAny<string>())).Returns(() => _stringToRecieve);
|
||||
serialMock.Setup(x => x.IsOpen).Returns(() => _isConnected);
|
||||
|
||||
_telescopeController = TelescopeController.Instance;
|
||||
_telescopeController.SerialPort = serialMock.Object;
|
||||
@@ -39,7 +38,6 @@ namespace MeadeAutostar497.UnitTests
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
_telescopeController.Connected = false;
|
||||
_telescopeController.Port = "COM1";
|
||||
}
|
||||
|
||||
@@ -59,7 +57,8 @@ namespace MeadeAutostar497.UnitTests
|
||||
[Test]
|
||||
public void ConnectedCanBeSetTrue()
|
||||
{
|
||||
stringToRecieve = "test#";
|
||||
_stringToRecieve = "test#";
|
||||
_isConnected = true;
|
||||
|
||||
_telescopeController.Connected = true;
|
||||
Assert.That(_telescopeController.Connected, Is.True);
|
||||
@@ -68,27 +67,30 @@ namespace MeadeAutostar497.UnitTests
|
||||
[Test]
|
||||
public void EnsureThatTheSerialCommunicationsAreSetCorrectly()
|
||||
{
|
||||
Assert.That(serialMock.Object.Connected, Is.False);
|
||||
|
||||
stringToRecieve = "test#";
|
||||
Assert.That(serialMock.Object.IsOpen, Is.False);
|
||||
|
||||
_stringToRecieve = "test#";
|
||||
_telescopeController.Connected = true;
|
||||
_isConnected = true;
|
||||
Assert.That(_telescopeController.Connected, Is.True);
|
||||
|
||||
Assert.That(serialMock.Object.DTREnable, Is.False);
|
||||
Assert.That(serialMock.Object.RTSEnable, Is.False);
|
||||
Assert.That(serialMock.Object.Speed, Is.EqualTo(SerialSpeed.ps9600));
|
||||
serialMock.Verify(x => x.Open(), Times.Once);
|
||||
|
||||
Assert.That(serialMock.Object.DtrEnable, Is.False);
|
||||
Assert.That(serialMock.Object.RtsEnable, Is.False);
|
||||
Assert.That(serialMock.Object.BaudRate, Is.EqualTo(9600));
|
||||
Assert.That(serialMock.Object.DataBits, Is.EqualTo(8));
|
||||
Assert.That(serialMock.Object.StopBits, Is.EqualTo(SerialStopBits.One));
|
||||
Assert.That(serialMock.Object.Parity, Is.EqualTo(SerialParity.None));
|
||||
Assert.That(serialMock.Object.StopBits, Is.EqualTo(StopBits.One));
|
||||
Assert.That(serialMock.Object.Parity, Is.EqualTo(Parity.None));
|
||||
Assert.That(serialMock.Object.PortName, Is.EqualTo(_telescopeController.Port));
|
||||
Assert.That(serialMock.Object.Connected, Is.True);
|
||||
Assert.That(serialMock.Object.IsOpen, Is.True);
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WhenOpensComPortToNonAutostarThrowException()
|
||||
{
|
||||
Assert.That(serialMock.Object.Connected, Is.False);
|
||||
Assert.That(serialMock.Object.IsOpen, Is.False);
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => { _telescopeController.Connected = true; });
|
||||
|
||||
Assert.That(exception.Message, Is.EqualTo("Failed to communicate with telescope."));
|
||||
@@ -99,11 +101,12 @@ namespace MeadeAutostar497.UnitTests
|
||||
[Test]
|
||||
public void CannotChangeSerialPortObjectWhenConnected()
|
||||
{
|
||||
stringToRecieve = "test#";
|
||||
_stringToRecieve = "test#";
|
||||
_isConnected = true;
|
||||
|
||||
_telescopeController.Connected = true;
|
||||
|
||||
Mock<ISerial> newSerialMock = new Mock<ISerial>();
|
||||
Mock<ISerialProcessor> newSerialMock = new Mock<ISerialProcessor>();
|
||||
|
||||
var exception = Assert.Throws<InvalidOperationException>( () => { _telescopeController.SerialPort = newSerialMock.Object; });
|
||||
|
||||
@@ -128,7 +131,8 @@ namespace MeadeAutostar497.UnitTests
|
||||
[Test]
|
||||
public void SettingPortToValidPortWhenConnectedFails()
|
||||
{
|
||||
stringToRecieve = "test#";
|
||||
_stringToRecieve = "test#";
|
||||
_isConnected = true;
|
||||
|
||||
_telescopeController.Connected = true;
|
||||
var exception = Assert.Throws<InvalidOperationException>( () => _telescopeController.Port = "COM2");
|
||||
|
||||
@@ -169,9 +169,9 @@ namespace ASCOM.MeadeAutostar497
|
||||
{
|
||||
CheckConnected("CommandBlind");
|
||||
// Call CommandString and return as soon as it finishes
|
||||
this.CommandString(command, raw);
|
||||
//this.CommandString(command, raw);
|
||||
// or
|
||||
//throw new ASCOM.MethodNotImplementedException("CommandBlind");
|
||||
throw new ASCOM.MethodNotImplementedException("CommandBlind");
|
||||
// DO NOT have both these sections! One or the other
|
||||
}
|
||||
|
||||
@@ -187,8 +187,11 @@ namespace ASCOM.MeadeAutostar497
|
||||
|
||||
public string CommandString(string command, bool raw)
|
||||
{
|
||||
// it's a good idea to put all the low level communication with the device here,
|
||||
// then all communication calls this function
|
||||
// you need something to ensure that only one command is in progress at a time
|
||||
CheckConnected("CommandString");
|
||||
return _telescopeController.CommandString(command, raw);
|
||||
throw new ASCOM.MethodNotImplementedException("CommandString");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
using System;
|
||||
using ASCOM.Utilities.Interfaces;
|
||||
using System.IO.Ports;
|
||||
|
||||
namespace ASCOM.MeadeAutostar497.Controller
|
||||
{
|
||||
public interface ITelescopeController
|
||||
{
|
||||
ISerial SerialPort { get; set; }
|
||||
ISerialProcessor SerialPort { get; set; }
|
||||
string Port { get; set; }
|
||||
bool Connected { get; set; }
|
||||
|
||||
string CommandString(string command, bool raw);
|
||||
bool Slewing { get; }
|
||||
DateTime utcDate { get; set; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using System.IO.Ports;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace ASCOM.MeadeAutostar497.Controller
|
||||
{
|
||||
[ComVisible(false)]
|
||||
public interface ISerialProcessor
|
||||
{
|
||||
bool IsOpen { get; }
|
||||
bool DtrEnable { get; set; }
|
||||
bool RtsEnable { get; set; }
|
||||
int BaudRate { get; set; }
|
||||
int DataBits { get; set; }
|
||||
StopBits StopBits { get; set; }
|
||||
Parity Parity { get; set; }
|
||||
string PortName { get; set; }
|
||||
string[] GetPortNames();
|
||||
void Open();
|
||||
void Close();
|
||||
|
||||
string CommandTerminated(string command, string terminator);
|
||||
char CommandChar(string command);
|
||||
string ReadTerminated(string terminator);
|
||||
}
|
||||
|
||||
[ComVisible(false)]
|
||||
public class SerialProcessor : ISerialProcessor
|
||||
{
|
||||
private SerialPort _serialPort = new SerialPort();
|
||||
private Mutex serialMutex = new Mutex();
|
||||
|
||||
public bool IsOpen => _serialPort.IsOpen;
|
||||
|
||||
public bool DtrEnable
|
||||
{
|
||||
get => _serialPort.DtrEnable;
|
||||
set => _serialPort.DtrEnable = value;
|
||||
}
|
||||
|
||||
public bool RtsEnable
|
||||
{
|
||||
get => _serialPort.RtsEnable;
|
||||
set => _serialPort.RtsEnable = value;
|
||||
}
|
||||
|
||||
public int BaudRate
|
||||
{
|
||||
get => _serialPort.BaudRate;
|
||||
set => _serialPort.BaudRate = value;
|
||||
}
|
||||
|
||||
public int DataBits
|
||||
{
|
||||
get => _serialPort.DataBits;
|
||||
set => _serialPort.DataBits = value;
|
||||
}
|
||||
|
||||
public StopBits StopBits
|
||||
{
|
||||
get => _serialPort.StopBits;
|
||||
set => _serialPort.StopBits = value;
|
||||
}
|
||||
|
||||
public Parity Parity
|
||||
{
|
||||
get => _serialPort.Parity;
|
||||
set => _serialPort.Parity = value;
|
||||
}
|
||||
|
||||
public string PortName
|
||||
{
|
||||
get => _serialPort.PortName;
|
||||
set => _serialPort.PortName = value;
|
||||
}
|
||||
|
||||
public string[] GetPortNames()
|
||||
{
|
||||
return SerialPort.GetPortNames();
|
||||
}
|
||||
|
||||
public void Open()
|
||||
{
|
||||
_serialPort.Open();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
_serialPort.Close();
|
||||
}
|
||||
|
||||
public string CommandTerminated(string command, string terminator)
|
||||
{
|
||||
serialMutex.WaitOne();
|
||||
try
|
||||
{
|
||||
_serialPort.Write(command);
|
||||
string result = _serialPort.ReadTo("#");
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
serialMutex.ReleaseMutex();
|
||||
}
|
||||
}
|
||||
|
||||
public char CommandChar(string command)
|
||||
{
|
||||
serialMutex.WaitOne();
|
||||
try
|
||||
{
|
||||
_serialPort.Write(command);
|
||||
var result = _serialPort.ReadChar();
|
||||
return Convert.ToChar(result);
|
||||
}
|
||||
finally
|
||||
{
|
||||
serialMutex.ReleaseMutex();
|
||||
}
|
||||
}
|
||||
|
||||
public string ReadTerminated(string terminator)
|
||||
{
|
||||
serialMutex.WaitOne();
|
||||
try
|
||||
{
|
||||
string result = _serialPort.ReadTo("#");
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
serialMutex.ReleaseMutex();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.IO.Ports;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using ASCOM.Utilities;
|
||||
@@ -14,12 +15,10 @@ namespace ASCOM.MeadeAutostar497.Controller
|
||||
|
||||
public static TelescopeController Instance => lazy.Value;
|
||||
|
||||
private Mutex serialMutex = new Mutex();
|
||||
|
||||
private ISerial _serialPort;
|
||||
public ISerial SerialPort
|
||||
private ISerialProcessor _serialPort;
|
||||
public ISerialProcessor SerialPort
|
||||
{
|
||||
get => _serialPort ?? (_serialPort = new Serial());
|
||||
get => _serialPort ?? (_serialPort = new SerialProcessor());
|
||||
set
|
||||
{
|
||||
if (_serialPort == value)
|
||||
@@ -27,7 +26,7 @@ namespace ASCOM.MeadeAutostar497.Controller
|
||||
|
||||
if (_serialPort != null)
|
||||
{
|
||||
if (_serialPort.Connected)
|
||||
if (_serialPort.IsOpen)
|
||||
throw new InvalidOperationException("Please disconnect before changing the serial engine.");
|
||||
}
|
||||
|
||||
@@ -55,12 +54,12 @@ namespace ASCOM.MeadeAutostar497.Controller
|
||||
|
||||
private bool ValidPort(string value)
|
||||
{
|
||||
return SerialPort.AvailableComPorts.Contains(value);
|
||||
return SerialPort.GetPortNames().Contains(value);
|
||||
}
|
||||
|
||||
public bool Connected
|
||||
{
|
||||
get => SerialPort.Connected;
|
||||
get => SerialPort.IsOpen;
|
||||
set
|
||||
{
|
||||
if (value == Connected)
|
||||
@@ -71,60 +70,48 @@ namespace ASCOM.MeadeAutostar497.Controller
|
||||
//Connecting
|
||||
try
|
||||
{
|
||||
SerialPort.DTREnable = false;
|
||||
SerialPort.RTSEnable = false;
|
||||
SerialPort.Speed = SerialSpeed.ps9600;
|
||||
SerialPort.DtrEnable = false;
|
||||
SerialPort.RtsEnable = false;
|
||||
SerialPort.BaudRate = 9600;
|
||||
SerialPort.DataBits = 8;
|
||||
SerialPort.StopBits = SerialStopBits.One;
|
||||
SerialPort.Parity = SerialParity.None;
|
||||
SerialPort.StopBits = StopBits.One;
|
||||
SerialPort.Parity = Parity.None;
|
||||
SerialPort.PortName = Port;
|
||||
SerialPort.Connected = true;
|
||||
SerialPort.Open();
|
||||
|
||||
TestConnectionActive();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
SerialPort.Connected = false;
|
||||
if (SerialPort.IsOpen)
|
||||
SerialPort.Close();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Disconnecting
|
||||
SerialPort.Connected = false;
|
||||
SerialPort.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TestConnectionActive()
|
||||
{
|
||||
var firmwareVersionNumber = CommandString("GVN");
|
||||
var firmwareVersionNumber = SerialPort.CommandTerminated(":GVN#", "#");
|
||||
if (string.IsNullOrEmpty(firmwareVersionNumber))
|
||||
{
|
||||
throw new InvalidOperationException("Failed to communicate with telescope.");
|
||||
}
|
||||
}
|
||||
|
||||
public string CommandString(string command)
|
||||
{
|
||||
return CommandString($"#:{command}#", false);
|
||||
}
|
||||
|
||||
public string CommandString(string command, bool raw)
|
||||
{
|
||||
// it's a good idea to put all the low level communication with the device here,
|
||||
// then all communication calls this function
|
||||
// you need something to ensure that only one command is in progress at a time
|
||||
return SerialCommand(command, true);
|
||||
}
|
||||
|
||||
public bool Slewing
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!Connected) return false;
|
||||
|
||||
var result = CommandString("D");
|
||||
var result = SerialPort.CommandTerminated("#:D#", "#");
|
||||
return result != string.Empty;
|
||||
}
|
||||
}
|
||||
@@ -133,8 +120,8 @@ namespace ASCOM.MeadeAutostar497.Controller
|
||||
{
|
||||
get
|
||||
{
|
||||
string telescopeDate = CommandString("GC");
|
||||
string telescopeTime = CommandString("GL");
|
||||
string telescopeDate = SerialPort.CommandTerminated("#:GC#", "#");
|
||||
string telescopeTime = SerialPort.CommandTerminated("#:GL#", "#");
|
||||
|
||||
int month = telescopeDate.Substring(0, 2).ToInteger();
|
||||
int day = telescopeDate.Substring(3, 2).ToInteger();
|
||||
@@ -156,40 +143,22 @@ namespace ASCOM.MeadeAutostar497.Controller
|
||||
set
|
||||
{
|
||||
//var result = SerialCommand(":SLHH:MM:SS#", true);
|
||||
var timeResult = SerialCommand($"#:SL{value:hh:mm:ss}#", true);
|
||||
if (timeResult != "1")
|
||||
var timeResult = SerialPort.CommandChar($"#:SL{value:hh:mm:ss}#");
|
||||
if (timeResult != '1')
|
||||
{
|
||||
throw new InvalidOperationException("Failed to set local time");
|
||||
}
|
||||
|
||||
var dateResult = SerialCommand($"#:SC{value:MM/dd/yy}#", true);
|
||||
if (dateResult.Substring(0,1) != "1")
|
||||
var dateResult = SerialPort.CommandChar($"#:SC{value:MM/dd/yy}#");
|
||||
if (dateResult != '1')
|
||||
{
|
||||
throw new InvalidOperationException("Failed to set local time");
|
||||
}
|
||||
|
||||
SerialPort.ReadTerminated("#");
|
||||
SerialPort.ReadTerminated("#");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private string SerialCommand(string command, bool expectsResult )
|
||||
{
|
||||
serialMutex.WaitOne();
|
||||
try
|
||||
{
|
||||
SerialPort.Transmit(command);
|
||||
if (expectsResult)
|
||||
{
|
||||
string result = SerialPort.ReceiveTerminated("#");
|
||||
|
||||
return result;
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
finally
|
||||
{
|
||||
SerialPort.ClearBuffers();
|
||||
serialMutex.ReleaseMutex();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +90,7 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="AscomClasses\Telescope.cs" />
|
||||
<Compile Include="Controller\ITelescopeController.cs" />
|
||||
<Compile Include="Controller\SerialProcessor.cs" />
|
||||
<Compile Include="Controller\TelescopeController.cs" />
|
||||
<Compile Include="StringExtensions.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
|
||||
Reference in New Issue
Block a user