1
0
mirror of https://github.com/DarthAffe/OBD.NET.git synced 2025-12-12 16:58:30 +00:00

211 lines
7.3 KiB
C#

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using OBD.NET.Common.Commands;
using OBD.NET.Common.Communication;
using OBD.NET.Common.Enums;
using OBD.NET.Common.Events;
using OBD.NET.Common.Events.EventArgs;
using OBD.NET.Common.Extensions;
using OBD.NET.Common.Logging;
using OBD.NET.Common.OBDData;
namespace OBD.NET.Common.Devices
{
public class ELM327 : SerialDevice
{
#region Properties & Fields
protected readonly Dictionary<Type, IDataEventManager> DataReceivedEventHandlers = new Dictionary<Type, IDataEventManager>();
protected static Dictionary<Type, byte> PidCache { get; } = new Dictionary<Type, byte>();
protected static Dictionary<byte, Type> DataTypeCache { get; } = new Dictionary<byte, Type>();
protected Mode Mode { get; set; } = Mode.ShowCurrentData; //TODO DarthAffe 26.06.2016: Implement different modes
#endregion
#region Events
public delegate void DataReceivedEventHandler<T>(object sender, DataReceivedEventArgs<T> args) where T : IOBDData;
public delegate void RawDataReceivedEventHandler(object sender, RawDataReceivedEventArgs args);
public event RawDataReceivedEventHandler RawDataReceived;
#endregion
#region Constructors
public ELM327(ISerialConnection connection, IOBDLogger logger = null)
: base(connection, logger: logger)
{ }
#endregion
#region Methods
public override async Task InitializeAsync()
{
await base.InitializeAsync();
InternalInitialize();
}
public override void Initialize()
{
base.Initialize();
InternalInitialize();
}
private void InternalInitialize()
{
Logger?.WriteLine("Initializing ...", OBDLogLevel.Debug);
try
{
Logger?.WriteLine("Resetting Device ...", OBDLogLevel.Debug);
SendCommand(ATCommand.ResetDevice);
Logger?.WriteLine("Turning Echo Off ...", OBDLogLevel.Debug);
SendCommand(ATCommand.EchoOff);
Logger?.WriteLine("Turning Linefeeds Off ...", OBDLogLevel.Debug);
SendCommand(ATCommand.LinefeedsOff);
Logger?.WriteLine("Turning Headers Off ...", OBDLogLevel.Debug);
SendCommand(ATCommand.HeadersOff);
Logger?.WriteLine("Turning Spaced Off ...", OBDLogLevel.Debug);
SendCommand(ATCommand.PrintSpacesOff);
Logger?.WriteLine("Setting the Protocol to 'Auto' ...", OBDLogLevel.Debug);
SendCommand(ATCommand.SetProtocolAuto);
}
// 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;
}
}
/// <summary>
/// Sends the AT command.
/// </summary>
/// <param name="command">The command.</param>
public virtual void SendCommand(ATCommand command) => SendCommand(command.Command);
/// <summary>
/// Requests the data and calls the handler
/// </summary>
/// <typeparam name="T"></typeparam>
public virtual void RequestData<T>()
where T : class, IOBDData, new()
{
Logger?.WriteLine("Requesting Type " + typeof(T).Name + " ...", OBDLogLevel.Debug);
byte pid = ResolvePid<T>();
RequestData(pid);
}
protected virtual void RequestData(byte pid)
{
Logger?.WriteLine("Requesting PID " + pid.ToString("X2") + " ...", OBDLogLevel.Debug);
SendCommand(((byte)Mode).ToString("X2") + pid.ToString("X2"));
}
/// <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);
CommandResult 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;
RawDataReceived?.Invoke(this, new RawDataReceivedEventArgs(message, timestamp));
if (message.Length > 4)
{
if (message[0] == '4')
{
byte mode = (byte)message[1].GetHexVal();
if (mode == (byte)Mode)
{
byte pid = (byte)message.Substring(2, 2).GetHexVal();
if (DataTypeCache.TryGetValue(pid, out Type dataType))
{
IOBDData obdData = (IOBDData)Activator.CreateInstance(dataType);
obdData.Load(message.Substring(4, message.Length - 4));
if (DataReceivedEventHandlers.TryGetValue(dataType, out IDataEventManager dataEventManager))
dataEventManager.RaiseEvent(this, obdData, timestamp);
return obdData;
}
}
}
}
return null;
}
protected virtual byte ResolvePid<T>()
where T : class, IOBDData, new()
{
if (!PidCache.TryGetValue(typeof(T), out byte pid))
{
T data = Activator.CreateInstance<T>();
pid = data.PID;
PidCache.Add(typeof(T), pid);
DataTypeCache.Add(pid, typeof(T));
}
return pid;
}
public override void Dispose() => Dispose(true);
public void Dispose(bool sendCloseProtocol)
{
try
{
if (sendCloseProtocol)
SendCommand(ATCommand.CloseProtocol);
}
catch { /* Well at least we tried ... */ }
DataReceivedEventHandlers.Clear();
base.Dispose();
}
public void SubscribeDataReceived<T>(DataReceivedEventHandler<T> eventHandler) where T : IOBDData
{
if (!DataReceivedEventHandlers.TryGetValue(typeof(T), out IDataEventManager eventManager))
DataReceivedEventHandlers.Add(typeof(T), (eventManager = new GenericDataEventManager<T>()));
((GenericDataEventManager<T>)eventManager).DataReceived += eventHandler;
}
public void UnsubscribeDataReceived<T>(DataReceivedEventHandler<T> eventHandler) where T : IOBDData
{
if (DataReceivedEventHandlers.TryGetValue(typeof(T), out IDataEventManager eventManager))
((GenericDataEventManager<T>)eventManager).DataReceived -= eventHandler;
}
#endregion
}
}