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:
2019-04-29 23:29:57 +01:00
parent da6b7ec55f
commit dc2a24ad25
6 changed files with 204 additions and 90 deletions
@@ -1,8 +1,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO.Ports;
using ASCOM; using ASCOM;
using ASCOM.MeadeAutostar497.Controller; using ASCOM.MeadeAutostar497.Controller;
using ASCOM.Utilities; using ASCOM.Utilities;
using ASCOM.Utilities.Interfaces;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
@@ -11,26 +11,25 @@ namespace MeadeAutostar497.UnitTests
[TestFixture] [TestFixture]
public class TelescopeControllerUnitTests public class TelescopeControllerUnitTests
{ {
private Mock<ISerial> serialMock; private Mock<ISerialProcessor> serialMock;
private readonly List<string> _availableComPorts = new List<string> { "COM1", "COM2", "COM3" }; private readonly List<string> _availableComPorts = new List<string> { "COM1", "COM2", "COM3" };
private TelescopeController _telescopeController; private TelescopeController _telescopeController;
private string transmittedString; private string _stringToRecieve = string.Empty;
private string stringToRecieve; private bool _isConnected = false;
[SetUp] [SetUp]
public void 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.SetupAllProperties();
serialMock.Setup(x => x.GetPortNames()).Returns( () => _availableComPorts.ToArray());
serialMock.Setup(x => x.AvailableComPorts).Returns( () => _availableComPorts.ToArray()); serialMock.Setup(x => x.CommandTerminated(It.IsAny<string>(), It.IsAny<string>())).Returns(() => _stringToRecieve);
serialMock.Setup(X => X.Transmit(It.IsAny<string>())).Callback<string>(str => { transmittedString = str; }); serialMock.Setup(x => x.IsOpen).Returns(() => _isConnected);
serialMock.Setup(X => X.Receive()).Returns(() => stringToRecieve);
_telescopeController = TelescopeController.Instance; _telescopeController = TelescopeController.Instance;
_telescopeController.SerialPort = serialMock.Object; _telescopeController.SerialPort = serialMock.Object;
@@ -39,7 +38,6 @@ namespace MeadeAutostar497.UnitTests
[TearDown] [TearDown]
public void TearDown() public void TearDown()
{ {
_telescopeController.Connected = false;
_telescopeController.Port = "COM1"; _telescopeController.Port = "COM1";
} }
@@ -59,7 +57,8 @@ namespace MeadeAutostar497.UnitTests
[Test] [Test]
public void ConnectedCanBeSetTrue() public void ConnectedCanBeSetTrue()
{ {
stringToRecieve = "test#"; _stringToRecieve = "test#";
_isConnected = true;
_telescopeController.Connected = true; _telescopeController.Connected = true;
Assert.That(_telescopeController.Connected, Is.True); Assert.That(_telescopeController.Connected, Is.True);
@@ -68,27 +67,30 @@ namespace MeadeAutostar497.UnitTests
[Test] [Test]
public void EnsureThatTheSerialCommunicationsAreSetCorrectly() public void EnsureThatTheSerialCommunicationsAreSetCorrectly()
{ {
Assert.That(serialMock.Object.Connected, Is.False); Assert.That(serialMock.Object.IsOpen, Is.False);
stringToRecieve = "test#";
_stringToRecieve = "test#";
_telescopeController.Connected = true; _telescopeController.Connected = true;
_isConnected = true;
Assert.That(_telescopeController.Connected, Is.True); Assert.That(_telescopeController.Connected, Is.True);
Assert.That(serialMock.Object.DTREnable, Is.False); serialMock.Verify(x => x.Open(), Times.Once);
Assert.That(serialMock.Object.RTSEnable, Is.False);
Assert.That(serialMock.Object.Speed, Is.EqualTo(SerialSpeed.ps9600)); 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.DataBits, Is.EqualTo(8));
Assert.That(serialMock.Object.StopBits, Is.EqualTo(SerialStopBits.One)); Assert.That(serialMock.Object.StopBits, Is.EqualTo(StopBits.One));
Assert.That(serialMock.Object.Parity, Is.EqualTo(SerialParity.None)); Assert.That(serialMock.Object.Parity, Is.EqualTo(Parity.None));
Assert.That(serialMock.Object.PortName, Is.EqualTo(_telescopeController.Port)); Assert.That(serialMock.Object.PortName, Is.EqualTo(_telescopeController.Port));
Assert.That(serialMock.Object.Connected, Is.True); Assert.That(serialMock.Object.IsOpen, Is.True);
} }
[Test] [Test]
public void WhenOpensComPortToNonAutostarThrowException() 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; }); var exception = Assert.Throws<InvalidOperationException>(() => { _telescopeController.Connected = true; });
Assert.That(exception.Message, Is.EqualTo("Failed to communicate with telescope.")); Assert.That(exception.Message, Is.EqualTo("Failed to communicate with telescope."));
@@ -99,11 +101,12 @@ namespace MeadeAutostar497.UnitTests
[Test] [Test]
public void CannotChangeSerialPortObjectWhenConnected() public void CannotChangeSerialPortObjectWhenConnected()
{ {
stringToRecieve = "test#"; _stringToRecieve = "test#";
_isConnected = true;
_telescopeController.Connected = 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; }); var exception = Assert.Throws<InvalidOperationException>( () => { _telescopeController.SerialPort = newSerialMock.Object; });
@@ -128,7 +131,8 @@ namespace MeadeAutostar497.UnitTests
[Test] [Test]
public void SettingPortToValidPortWhenConnectedFails() public void SettingPortToValidPortWhenConnectedFails()
{ {
stringToRecieve = "test#"; _stringToRecieve = "test#";
_isConnected = true;
_telescopeController.Connected = true; _telescopeController.Connected = true;
var exception = Assert.Throws<InvalidOperationException>( () => _telescopeController.Port = "COM2"); var exception = Assert.Throws<InvalidOperationException>( () => _telescopeController.Port = "COM2");
+6 -3
View File
@@ -169,9 +169,9 @@ namespace ASCOM.MeadeAutostar497
{ {
CheckConnected("CommandBlind"); CheckConnected("CommandBlind");
// Call CommandString and return as soon as it finishes // Call CommandString and return as soon as it finishes
this.CommandString(command, raw); //this.CommandString(command, raw);
// or // or
//throw new ASCOM.MethodNotImplementedException("CommandBlind"); throw new ASCOM.MethodNotImplementedException("CommandBlind");
// DO NOT have both these sections! One or the other // DO NOT have both these sections! One or the other
} }
@@ -187,8 +187,11 @@ namespace ASCOM.MeadeAutostar497
public string CommandString(string command, bool raw) 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"); CheckConnected("CommandString");
return _telescopeController.CommandString(command, raw); throw new ASCOM.MethodNotImplementedException("CommandString");
} }
public void Dispose() public void Dispose()
@@ -1,15 +1,14 @@
using System; using System;
using ASCOM.Utilities.Interfaces; using System.IO.Ports;
namespace ASCOM.MeadeAutostar497.Controller namespace ASCOM.MeadeAutostar497.Controller
{ {
public interface ITelescopeController public interface ITelescopeController
{ {
ISerial SerialPort { get; set; } ISerialProcessor SerialPort { get; set; }
string Port { get; set; } string Port { get; set; }
bool Connected { get; set; } bool Connected { get; set; }
string CommandString(string command, bool raw);
bool Slewing { get; } bool Slewing { get; }
DateTime utcDate { get; set; } 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;
using System.Configuration; using System.Configuration;
using System.Data; using System.Data;
using System.IO.Ports;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using ASCOM.Utilities; using ASCOM.Utilities;
@@ -14,12 +15,10 @@ namespace ASCOM.MeadeAutostar497.Controller
public static TelescopeController Instance => lazy.Value; public static TelescopeController Instance => lazy.Value;
private Mutex serialMutex = new Mutex(); private ISerialProcessor _serialPort;
public ISerialProcessor SerialPort
private ISerial _serialPort;
public ISerial SerialPort
{ {
get => _serialPort ?? (_serialPort = new Serial()); get => _serialPort ?? (_serialPort = new SerialProcessor());
set set
{ {
if (_serialPort == value) if (_serialPort == value)
@@ -27,7 +26,7 @@ namespace ASCOM.MeadeAutostar497.Controller
if (_serialPort != null) if (_serialPort != null)
{ {
if (_serialPort.Connected) if (_serialPort.IsOpen)
throw new InvalidOperationException("Please disconnect before changing the serial engine."); throw new InvalidOperationException("Please disconnect before changing the serial engine.");
} }
@@ -55,12 +54,12 @@ namespace ASCOM.MeadeAutostar497.Controller
private bool ValidPort(string value) private bool ValidPort(string value)
{ {
return SerialPort.AvailableComPorts.Contains(value); return SerialPort.GetPortNames().Contains(value);
} }
public bool Connected public bool Connected
{ {
get => SerialPort.Connected; get => SerialPort.IsOpen;
set set
{ {
if (value == Connected) if (value == Connected)
@@ -71,60 +70,48 @@ namespace ASCOM.MeadeAutostar497.Controller
//Connecting //Connecting
try try
{ {
SerialPort.DTREnable = false; SerialPort.DtrEnable = false;
SerialPort.RTSEnable = false; SerialPort.RtsEnable = false;
SerialPort.Speed = SerialSpeed.ps9600; SerialPort.BaudRate = 9600;
SerialPort.DataBits = 8; SerialPort.DataBits = 8;
SerialPort.StopBits = SerialStopBits.One; SerialPort.StopBits = StopBits.One;
SerialPort.Parity = SerialParity.None; SerialPort.Parity = Parity.None;
SerialPort.PortName = Port; SerialPort.PortName = Port;
SerialPort.Connected = true; SerialPort.Open();
TestConnectionActive(); TestConnectionActive();
} }
catch (Exception) catch (Exception)
{ {
SerialPort.Connected = false; if (SerialPort.IsOpen)
SerialPort.Close();
throw; throw;
} }
} }
else else
{ {
//Disconnecting //Disconnecting
SerialPort.Connected = false; SerialPort.Close();
} }
} }
} }
private void TestConnectionActive() private void TestConnectionActive()
{ {
var firmwareVersionNumber = CommandString("GVN"); var firmwareVersionNumber = SerialPort.CommandTerminated(":GVN#", "#");
if (string.IsNullOrEmpty(firmwareVersionNumber)) if (string.IsNullOrEmpty(firmwareVersionNumber))
{ {
throw new InvalidOperationException("Failed to communicate with telescope."); 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 public bool Slewing
{ {
get get
{ {
if (!Connected) return false; if (!Connected) return false;
var result = CommandString("D"); var result = SerialPort.CommandTerminated("#:D#", "#");
return result != string.Empty; return result != string.Empty;
} }
} }
@@ -133,8 +120,8 @@ namespace ASCOM.MeadeAutostar497.Controller
{ {
get get
{ {
string telescopeDate = CommandString("GC"); string telescopeDate = SerialPort.CommandTerminated("#:GC#", "#");
string telescopeTime = CommandString("GL"); string telescopeTime = SerialPort.CommandTerminated("#:GL#", "#");
int month = telescopeDate.Substring(0, 2).ToInteger(); int month = telescopeDate.Substring(0, 2).ToInteger();
int day = telescopeDate.Substring(3, 2).ToInteger(); int day = telescopeDate.Substring(3, 2).ToInteger();
@@ -156,40 +143,22 @@ namespace ASCOM.MeadeAutostar497.Controller
set set
{ {
//var result = SerialCommand(":SLHH:MM:SS#", true); //var result = SerialCommand(":SLHH:MM:SS#", true);
var timeResult = SerialCommand($"#:SL{value:hh:mm:ss}#", true); var timeResult = SerialPort.CommandChar($"#:SL{value:hh:mm:ss}#");
if (timeResult != "1") if (timeResult != '1')
{ {
throw new InvalidOperationException("Failed to set local time"); throw new InvalidOperationException("Failed to set local time");
} }
var dateResult = SerialCommand($"#:SC{value:MM/dd/yy}#", true); var dateResult = SerialPort.CommandChar($"#:SC{value:MM/dd/yy}#");
if (dateResult.Substring(0,1) != "1") if (dateResult != '1')
{ {
throw new InvalidOperationException("Failed to set local time"); 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();
}
}
} }
} }
+1
View File
@@ -90,6 +90,7 @@
<ItemGroup> <ItemGroup>
<Compile Include="AscomClasses\Telescope.cs" /> <Compile Include="AscomClasses\Telescope.cs" />
<Compile Include="Controller\ITelescopeController.cs" /> <Compile Include="Controller\ITelescopeController.cs" />
<Compile Include="Controller\SerialProcessor.cs" />
<Compile Include="Controller\TelescopeController.cs" /> <Compile Include="Controller\TelescopeController.cs" />
<Compile Include="StringExtensions.cs" /> <Compile Include="StringExtensions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />