1
0
mirror of https://github.com/DarthAffe/OBD.NET.git synced 2025-12-13 09:18:31 +00:00

refactoring SerialDevice

This commit is contained in:
Roman Lumetsberger 2017-05-08 20:03:55 +02:00
parent 9d6d1cd36b
commit 02bd8630a2
6 changed files with 114 additions and 155 deletions

View File

@ -29,13 +29,13 @@ namespace OBD.NET.Communication
/// <summary> /// <summary>
/// Connects the serial port. /// Connects the serial port.
/// </summary> /// </summary>
bool Connect(); void Connect();
/// <summary> /// <summary>
/// Connects the serial port async /// Connects the serial port async
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
Task<bool> ConnectAsync(); Task ConnectAsync();
/// <summary> /// <summary>

View File

@ -80,51 +80,11 @@ namespace OBD.NET.Devices
} }
} }
public override async Task InitializeAsync()
{
await base.InitializeAsync();
Logger?.WriteLine("Initializing ...", OBDLogLevel.Debug);
try
{
Logger?.WriteLine("Resetting Device ...", OBDLogLevel.Debug);
await SendCommandAsync(ATCommand.ResetDevice);
Logger?.WriteLine("Turning Echo Off ...", OBDLogLevel.Debug);
await SendCommandAsync(ATCommand.EchoOff);
Logger?.WriteLine("Turning Linefeeds Off ...", OBDLogLevel.Debug);
await SendCommandAsync(ATCommand.LinefeedsOff);
Logger?.WriteLine("Turning Headers Off ...", OBDLogLevel.Debug);
await SendCommandAsync(ATCommand.HeadersOff);
Logger?.WriteLine("Turning Spaced Off ...", OBDLogLevel.Debug);
await SendCommandAsync(ATCommand.PrintSpacesOff);
Logger?.WriteLine("Setting the Protocol to 'Auto' ...", OBDLogLevel.Debug);
await SendCommandAsync(ATCommand.SetProtocolAuto); //TODO rewrite
}
// DarthAffe 21.02.2017: This seems to happen sometimes, i don't know why - just retry.
catch
{
Logger?.WriteLine("Failed to initialize the device!", OBDLogLevel.Error);
throw;
}
}
public virtual void SendCommand(ATCommand command) public virtual void SendCommand(ATCommand command)
{ {
SendCommand(command.Command); SendCommand(command.Command);
} }
public virtual async Task SendCommandAsync(ATCommand command)
{
await SendCommandAsync(command.Command);
}
public virtual void RequestData<T>() public virtual void RequestData<T>()
where T : class, IOBDData, new() where T : class, IOBDData, new()
{ {
@ -134,27 +94,12 @@ namespace OBD.NET.Devices
RequestData(pid); RequestData(pid);
} }
public virtual async Task RequestDataAsync<T>()
where T : class, IOBDData, new()
{
Logger?.WriteLine("Requesting Type " + typeof(T).Name + " ...", OBDLogLevel.Debug);
byte pid = ResolvePid<T>();
await RequestDataAsync(pid);
}
protected virtual void RequestData(byte pid) protected virtual void RequestData(byte pid)
{ {
Logger?.WriteLine("Requesting PID " + pid.ToString("X2") + " ...", OBDLogLevel.Debug); Logger?.WriteLine("Requesting PID " + pid.ToString("X2") + " ...", OBDLogLevel.Debug);
SendCommand(((byte)Mode).ToString("X2") + pid.ToString("X2")); SendCommand(((byte)Mode).ToString("X2") + pid.ToString("X2"));
} }
protected virtual async Task RequestDataAsync(byte pid)
{
Logger?.WriteLine("Requesting PID " + pid.ToString("X2") + " ...", OBDLogLevel.Debug);
await SendCommandAsync(((byte)Mode).ToString("X2") + pid.ToString("X2"));
}
protected override void ProcessMessage(string message) protected override void ProcessMessage(string message)
{ {
DateTime timestamp = DateTime.Now; DateTime timestamp = DateTime.Now;

View File

@ -4,6 +4,9 @@ using OBD.NET.Exceptions;
using OBD.NET.Logging; using OBD.NET.Logging;
using System.Threading.Tasks; using System.Threading.Tasks;
using OBD.NET.Common.Communication.EventArgs; using OBD.NET.Common.Communication.EventArgs;
using System.Text;
using System.Threading;
using System.Collections.Concurrent;
namespace OBD.NET.Devices namespace OBD.NET.Devices
{ {
@ -12,7 +15,14 @@ namespace OBD.NET.Devices
/// </summary> /// </summary>
public abstract class SerialDevice : IDisposable public abstract class SerialDevice : IDisposable
{ {
private System.Collections.Generic.Queue<string> commandQueue; private BlockingCollection<string> commandQueue;
private readonly StringBuilder _lineBuffer = new StringBuilder();
private readonly AutoResetEvent commandFinishedEvent = new AutoResetEvent(true);
private Task commandWorkerTask;
private CancellationTokenSource commandCancellationToken;
/// <summary> /// <summary>
/// Logger instance /// Logger instance
@ -34,7 +44,7 @@ namespace OBD.NET.Devices
private SerialDevice() private SerialDevice()
{ {
commandQueue = new System.Collections.Generic.Queue<string>(); commandQueue = new BlockingCollection<string>();
} }
protected SerialDevice(ISerialConnection connection, char terminator = '\r', IOBDLogger logger = null) protected SerialDevice(ISerialConnection connection, char terminator = '\r', IOBDLogger logger = null)
@ -50,11 +60,6 @@ namespace OBD.NET.Devices
#endregion #endregion
private void OnDataReceived(object sender, DataReceivedEventArgs e)
{
}
#region Methods #region Methods
@ -63,22 +68,21 @@ namespace OBD.NET.Devices
/// </summary> /// </summary>
public virtual void Initialize() public virtual void Initialize()
{ {
Connection.Connect(); Connection.Connect();
CheckConnection(); CheckConnectionAndStartWorker();
} }
/// <summary> /// <summary>
/// Initializes the device async /// Initializes the device asynchronously
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public virtual async Task InitializeAsync() public virtual async Task InitializeAsync()
{ {
await Connection.ConnectAsync(); await Connection.ConnectAsync();
CheckConnection(); CheckConnectionAndStartWorker();
} }
private void CheckConnection() private void CheckConnectionAndStartWorker()
{ {
if (!Connection.IsOpen) if (!Connection.IsOpen)
{ {
@ -86,10 +90,26 @@ namespace OBD.NET.Devices
throw new SerialException("Failed to open Serial-Connection."); throw new SerialException("Failed to open Serial-Connection.");
} }
else else
{
Logger?.WriteLine("Opened Serial-Connection!", OBDLogLevel.Debug); Logger?.WriteLine("Opened Serial-Connection!", OBDLogLevel.Debug);
} }
commandWorkerTask = Task.Factory.StartNew(CommandWorker);
}
private void CommandWorker()
{
while (!commandCancellationToken.IsCancellationRequested)
{
string command = null;
commandQueue.TryTake(out command, Timeout.Infinite, commandCancellationToken.Token);
Logger?.WriteLine("Writing Command: '" + command.Replace('\r', '\'') + "'", OBDLogLevel.Verbose);
Connection.Write(Encoding.ASCII.GetBytes(command));
//wait for command to finish
commandFinishedEvent.WaitOne();
}
}
protected virtual void SendCommand(string command) protected virtual void SendCommand(string command)
{ {
@ -100,9 +120,7 @@ namespace OBD.NET.Devices
command = PrepareCommand(command); command = PrepareCommand(command);
Logger?.WriteLine("Queuing Command: '" + command.Replace('\r', '\'') + "'", OBDLogLevel.Verbose); Logger?.WriteLine("Queuing Command: '" + command.Replace('\r', '\'') + "'", OBDLogLevel.Verbose);
commandQueue.Enqueue(command); commandQueue.Add(command);
// Logger?.WriteLine("Writing Command: '" + command.Replace('\r', '\'') + "'", OBDLogLevel.Verbose);
// Connection.Write(command);
} }
/// <summary> /// <summary>
@ -120,6 +138,40 @@ namespace OBD.NET.Devices
return command; return command;
} }
private void OnDataReceived(object sender, DataReceivedEventArgs e)
{
for (int i = 0; i < e.Count; i++)
{
char c = (char)e.Data[i];
switch (c)
{
case '\r':
FinishLine();
break;
case '>':
commandFinishedEvent.Set();
break;
case '\n':
case (char)0x00:
break; // ignore
default:
_lineBuffer.Append(c);
break;
}
}
}
private void FinishLine()
{
string line = _lineBuffer.ToString();
_lineBuffer.Clear();
ProcessMessage(line);
}
private void SerialMessageReceived(object sender, string message) private void SerialMessageReceived(object sender, string message)
{ {
if (string.IsNullOrWhiteSpace(message)) return; if (string.IsNullOrWhiteSpace(message)) return;

View File

@ -0,0 +1,20 @@
using OBD.NET.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace OBD.NET.Common.Logging
{
/// <summary>
/// Simple debug logger
/// </summary>
/// <seealso cref="OBD.NET.Logging.IOBDLogger" />
public class OBDDebugLogger : IOBDLogger
{
public void WriteLine(string text, OBDLogLevel level)
{
Debug.WriteLine($"{level}: {text}");
}
}
}

View File

@ -3,6 +3,7 @@ using System.Runtime.InteropServices.WindowsRuntime;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using OBD.NET.Common.Communication.EventArgs;
using Windows.Devices.Bluetooth.Rfcomm; using Windows.Devices.Bluetooth.Rfcomm;
using Windows.Networking.Sockets; using Windows.Networking.Sockets;
using Windows.Storage.Streams; using Windows.Storage.Streams;
@ -16,7 +17,6 @@ namespace OBD.NET.Communication
private DataWriter _writer; private DataWriter _writer;
private readonly byte[] _readBuffer = new byte[1024]; private readonly byte[] _readBuffer = new byte[1024];
private readonly StringBuilder _lineBuffer = new StringBuilder();
private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
private Task _readerTask; private Task _readerTask;
@ -32,7 +32,7 @@ namespace OBD.NET.Communication
/// <summary> /// <summary>
/// Occurs when a full line was received /// Occurs when a full line was received
/// </summary> /// </summary>
public event EventHandler<string> MessageReceived; public event EventHandler<DataReceivedEventArgs> DataReceived;
/// <summary> /// <summary>
/// Connects the serial port. /// Connects the serial port.
@ -74,7 +74,7 @@ namespace OBD.NET.Communication
/// </summary> /// </summary>
/// <param name="text">The text.</param> /// <param name="text">The text.</param>
/// <exception cref="System.NotImplementedException">Synchronous operations not supported</exception> /// <exception cref="System.NotImplementedException">Synchronous operations not supported</exception>
public void Write(string text) public void Write(byte[] data)
{ {
throw new NotImplementedException("Synchronous operations not supported"); throw new NotImplementedException("Synchronous operations not supported");
} }
@ -84,9 +84,9 @@ namespace OBD.NET.Communication
/// </summary> /// </summary>
/// <param name="text">The text.</param> /// <param name="text">The text.</param>
/// <returns></returns> /// <returns></returns>
public async Task WriteAsync(string text) public async Task WriteAsync(byte[] data)
{ {
_writer.WriteString(text); _writer.WriteBytes(data);
await _writer.StoreAsync(); await _writer.StoreAsync();
await _writer.FlushAsync(); await _writer.FlushAsync();
} }
@ -107,37 +107,9 @@ namespace OBD.NET.Communication
private void SerialPortOnDataReceived(IBuffer buffer) private void SerialPortOnDataReceived(IBuffer buffer)
{ {
DataReceived?.Invoke(this, new DataReceivedEventArgs((int)buffer.Length, _readBuffer));
for (uint i = 0; i < buffer.Length; i++)
{
char c = (char)buffer.GetByte(i);
switch (c)
{
case '\r':
FinishLine();
break;
case '>':
continue; //ignore
case '\n':
case (char)0x00:
break; // ignore
default:
_lineBuffer.Append(c);
break;
}
}
} }
private void FinishLine()
{
string line = _lineBuffer.ToString();
_lineBuffer.Clear();
MessageReceived?.Invoke(this, line);
}
public void Dispose() public void Dispose()
{ {
@ -146,6 +118,5 @@ namespace OBD.NET.Communication
_socket?.Dispose(); _socket?.Dispose();
} }
} }
} }

View File

@ -3,6 +3,7 @@ using System.IO.Ports;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using OBD.NET.Common.Communication.EventArgs;
namespace OBD.NET.Communication namespace OBD.NET.Communication
{ {
@ -24,7 +25,7 @@ namespace OBD.NET.Communication
#region Events #region Events
public event EventHandler<string> MessageReceived; public event EventHandler<DataReceivedEventArgs> DataReceived;
#endregion #endregion
@ -52,52 +53,14 @@ namespace OBD.NET.Communication
public void Connect() public void Connect()
{ {
_serialPort.Open(); _serialPort.Open();
Thread.Sleep(5000);
Write("\r");
}
public void Write(string text)
{
if (!_hasPrompt.WaitOne(_timeout))
throw new TimeoutException("No prompt received");
_serialPort.Write(text);
} }
private void SerialPortOnDataReceived(object sender, SerialDataReceivedEventArgs serialDataReceivedEventArgs) private void SerialPortOnDataReceived(object sender, SerialDataReceivedEventArgs serialDataReceivedEventArgs)
{ {
int count = _serialPort.Read(_readBuffer, 0, _serialPort.BytesToRead); int count = _serialPort.Read(_readBuffer, 0, _serialPort.BytesToRead);
for (int i = 0; i < count; i++) DataReceived?.Invoke(this,new DataReceivedEventArgs(count, _readBuffer));
{
char c = (char)_readBuffer[i];
switch (c)
{
case '\r':
FinishLine();
break;
case '>':
_hasPrompt.Set();
break;
case '\n':
case (char)0x00:
break; // ignore
default:
_lineBuffer.Append(c);
break;
}
}
} }
private void FinishLine()
{
string line = _lineBuffer.ToString();
_lineBuffer.Clear();
MessageReceived?.Invoke(this, line);
}
public void Dispose() public void Dispose()
{ {
@ -109,11 +72,19 @@ namespace OBD.NET.Communication
throw new NotSupportedException("Asynchronous operations not supported"); throw new NotSupportedException("Asynchronous operations not supported");
} }
public Task WriteAsync(string text) public Task WriteAsync(byte[] data)
{ {
throw new NotSupportedException("Asynchronous operations not supported"); throw new NotSupportedException("Asynchronous operations not supported");
} }
public void Write(byte[] data)
{
_serialPort.Write(data, 0, data.Length);
}
#endregion #endregion
} }
} }