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

Implemented RequestDataAsync

This commit is contained in:
Roman Lumetsberger 2017-05-20 18:02:13 +02:00
parent f38a867a99
commit fa3fb7b756
5 changed files with 159 additions and 15 deletions

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace OBD.NET.Common.Devices
{
/// <summary>
/// Class used for queued command
/// </summary>
public class QueuedCommand
{
/// <summary>
/// Initializes a new instance
/// </summary>
/// <param name="commandText"></param>
public QueuedCommand(string commandText)
{
CommandResult = new CommandResult();
CommandText = commandText;
}
public string CommandText { get; set; }
public CommandResult CommandResult { get; }
}
}

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Text;
using OBD.NET.Util;
namespace OBD.NET.Common.Devices
{
public class CommandResult
{
public CommandResult()
{
WaitHandle = new AsyncManualResetEvent();
}
public object Result { get; set; }
public AsyncManualResetEvent WaitHandle { get; }
}
}

View File

@ -89,13 +89,21 @@ namespace OBD.NET.Devices
throw; throw;
} }
} }
/// <summary>
/// Sends the AT command.
/// </summary>
/// <param name="command">The command.</param>
public virtual void SendCommand(ATCommand command) public virtual void SendCommand(ATCommand command)
{ {
SendCommand(command.Command); SendCommand(command.Command);
} }
/// <summary>
/// Requests the data and calls the handler
/// </summary>
/// <typeparam name="T"></typeparam>
public virtual void RequestData<T>() public virtual void RequestData<T>()
where T : class, IOBDData, new() where T : class, IOBDData, new()
{ {
@ -111,13 +119,33 @@ namespace OBD.NET.Devices
SendCommand(((byte)Mode).ToString("X2") + pid.ToString("X2")); SendCommand(((byte)Mode).ToString("X2") + pid.ToString("X2"));
} }
protected override void ProcessMessage(string message) /// <summary>
/// Requests the data asynchronous and return the data when available
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public virtual async Task<T> RequestDataAsync<T>()
where T : class, IOBDData, new()
{
Logger?.WriteLine("Requesting Type " + typeof(T).Name + " ...", OBDLogLevel.Debug);
byte pid = ResolvePid<T>();
Logger?.WriteLine("Requesting PID " + pid.ToString("X2") + " ...", OBDLogLevel.Debug);
var result = SendCommand(((byte)Mode).ToString("X2") + pid.ToString("X2"));
await result.WaitHandle.WaitAsync();
return result.Result as T;
}
protected override object ProcessMessage(string message)
{ {
DateTime timestamp = DateTime.Now; DateTime timestamp = DateTime.Now;
RawDataReceived?.Invoke(this, new RawDataReceivedEventArgs(message, timestamp)); RawDataReceived?.Invoke(this, new RawDataReceivedEventArgs(message, timestamp));
if (message.Length > 4) if (message.Length > 4)
{
if (message[0] == '4') if (message[0] == '4')
{ {
byte mode = (byte)message[1].GetHexVal(); byte mode = (byte)message[1].GetHexVal();
@ -129,13 +157,17 @@ namespace OBD.NET.Devices
{ {
IOBDData obdData = (IOBDData)Activator.CreateInstance(dataType); IOBDData obdData = (IOBDData)Activator.CreateInstance(dataType);
obdData.Load(message.Substring(4, message.Length - 4)); obdData.Load(message.Substring(4, message.Length - 4));
IDataEventManager dataEventManager; IDataEventManager dataEventManager;
if (_dataReceivedEventHandlers.TryGetValue(dataType, out dataEventManager)) if (_dataReceivedEventHandlers.TryGetValue(dataType, out dataEventManager))
dataEventManager.RaiseEvent(this, obdData, timestamp); dataEventManager.RaiseEvent(this, obdData, timestamp);
return obdData;
} }
} }
} }
}
return null;
} }
protected virtual byte ResolvePid<T>() protected virtual byte ResolvePid<T>()

View File

@ -7,6 +7,7 @@ using OBD.NET.Common.Communication.EventArgs;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using OBD.NET.Common.Devices;
namespace OBD.NET.Devices namespace OBD.NET.Devices
{ {
@ -15,7 +16,7 @@ namespace OBD.NET.Devices
/// </summary> /// </summary>
public abstract class SerialDevice : IDisposable public abstract class SerialDevice : IDisposable
{ {
private BlockingCollection<string> commandQueue; private BlockingCollection<QueuedCommand> commandQueue;
private readonly StringBuilder _lineBuffer = new StringBuilder(); private readonly StringBuilder _lineBuffer = new StringBuilder();
@ -24,6 +25,8 @@ namespace OBD.NET.Devices
private Task commandWorkerTask; private Task commandWorkerTask;
private CancellationTokenSource commandCancellationToken; private CancellationTokenSource commandCancellationToken;
protected QueuedCommand currentCommand;
/// <summary> /// <summary>
/// Logger instance /// Logger instance
@ -48,7 +51,7 @@ namespace OBD.NET.Devices
/// </summary> /// </summary>
private SerialDevice() private SerialDevice()
{ {
commandQueue = new BlockingCollection<string>(); commandQueue = new BlockingCollection<QueuedCommand>();
} }
/// <summary> /// <summary>
@ -117,16 +120,20 @@ namespace OBD.NET.Devices
/// </summary> /// </summary>
/// <param name="command">command string</param> /// <param name="command">command string</param>
/// <exception cref="System.InvalidOperationException">Not connected</exception> /// <exception cref="System.InvalidOperationException">Not connected</exception>
protected virtual void SendCommand(string command) protected virtual CommandResult SendCommand(string command)
{ {
if (!Connection.IsOpen) if (!Connection.IsOpen)
{ {
throw new InvalidOperationException("Not connected"); throw new InvalidOperationException("Not connected");
} }
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.Add(command);
var cmd = new QueuedCommand(command);
commandQueue.Add(cmd);
return cmd.CommandResult;
} }
/// <summary> /// <summary>
@ -161,6 +168,7 @@ namespace OBD.NET.Devices
break; break;
case '>': case '>':
currentCommand.CommandResult.WaitHandle.Set();
commandFinishedEvent.Set(); commandFinishedEvent.Set();
break; break;
@ -186,14 +194,25 @@ namespace OBD.NET.Devices
if (string.IsNullOrWhiteSpace(line)) return; if (string.IsNullOrWhiteSpace(line)) return;
Logger?.WriteLine("Response: '" + line + "'", OBDLogLevel.Verbose); Logger?.WriteLine("Response: '" + line + "'", OBDLogLevel.Verbose);
ProcessMessage(line); InternalProcessMessage(line);
}
/// <summary>
/// Process message and sets the result
/// </summary>
/// <param name="message">The message.</param>
private void InternalProcessMessage(string message)
{
var data = ProcessMessage(message);
currentCommand.CommandResult.Result = data;
} }
/// <summary> /// <summary>
/// Processes the message. /// Processes the message.
/// </summary> /// </summary>
/// <param name="message">message received</param> /// <param name="message">message received</param>
protected abstract void ProcessMessage(string message); /// <returns>result data</returns>
protected abstract object ProcessMessage(string message);
/// <summary> /// <summary>
/// Worker method for sending commands /// Worker method for sending commands
@ -202,18 +221,18 @@ namespace OBD.NET.Devices
{ {
while (!commandCancellationToken.IsCancellationRequested) while (!commandCancellationToken.IsCancellationRequested)
{ {
string command = null; currentCommand = null;
if(commandQueue.TryTake(out command, Timeout.Infinite, commandCancellationToken.Token)) if(commandQueue.TryTake(out currentCommand, Timeout.Infinite, commandCancellationToken.Token))
{ {
Logger?.WriteLine("Writing Command: '" + command.Replace('\r', '\'') + "'", OBDLogLevel.Verbose); Logger?.WriteLine("Writing Command: '" + currentCommand.CommandText.Replace('\r', '\'') + "'", OBDLogLevel.Verbose);
if (Connection.IsAsync) if (Connection.IsAsync)
{ {
await Connection.WriteAsync(Encoding.ASCII.GetBytes(command)); await Connection.WriteAsync(Encoding.ASCII.GetBytes(currentCommand.CommandText));
} }
else else
{ {
Connection.Write(Encoding.ASCII.GetBytes(command)); Connection.Write(Encoding.ASCII.GetBytes(currentCommand.CommandText));
} }
//wait for command to finish //wait for command to finish

View File

@ -0,0 +1,48 @@
using System.Threading;
using System.Threading.Tasks;
namespace OBD.NET.Util
{
/// <summary>
/// Notifies one or more waiting awaiters that an event has occurred
/// </summary>
public class AsyncManualResetEvent
{
private volatile TaskCompletionSource<bool> _tcs = new TaskCompletionSource<bool>();
/// <summary>
/// Waits the async.
/// </summary>
/// <returns></returns>
public Task WaitAsync()
{
return _tcs.Task;
}
//public void Set() { m_tcs.TrySetResult(true); }
/// <summary>
/// Sets the state of the event to signaled, allowing one or more waiting awaiters to proceed.
/// </summary>
public void Set()
{
var tcs = _tcs;
Task.Factory.StartNew(s => ((TaskCompletionSource<bool>)s).TrySetResult(true),
tcs, CancellationToken.None, TaskCreationOptions.PreferFairness, TaskScheduler.Default);
tcs.Task.Wait();
}
/// <summary>
/// Sets the state of the event to nonsignaled, causing awaiters to block.
/// </summary>
public void Reset()
{
while (true)
{
var tcs = _tcs;
if (!tcs.Task.IsCompleted ||
Interlocked.CompareExchange(ref _tcs, new TaskCompletionSource<bool>(), tcs) == tcs)
return;
}
}
}
}