diff --git a/OBD.NET/OBD.NET/Commands/ATCommand.cs b/OBD.NET/OBD.NET/Commands/ATCommand.cs new file mode 100644 index 0000000..6267372 --- /dev/null +++ b/OBD.NET/OBD.NET/Commands/ATCommand.cs @@ -0,0 +1,61 @@ +namespace OBD.NET.Commands +{ + public class ATCommand + { + #region Values + + //TODO DarthAffe 26.06.2016: Implement all commands + + public static readonly ATCommand RepeatLastCommand = new ATCommand("\r"); + public static readonly ATCommand ResetDevice = new ATCommand("ATZ"); + public static readonly ATCommand ReadVoltage = new ATCommand("ATRV"); + public static readonly ATCommand EchoOn = new ATCommand("ATE1", "^OK$"); + public static readonly ATCommand EchoOff = new ATCommand("ATE0", "^OK$"); + public static readonly ATCommand HeadersOn = new ATCommand("ATH1", "^OK$"); + public static readonly ATCommand HeadersOff = new ATCommand("ATH0", "^OK$"); + public static readonly ATCommand PrintSpacesOn = new ATCommand("ATS1", "^OK$"); + public static readonly ATCommand PrintSpacesOff = new ATCommand("ATS0", "^OK$"); + public static readonly ATCommand LinefeedsOn = new ATCommand("ATL1", "^OK$"); + public static readonly ATCommand LinefeedsOff = new ATCommand("ATL0", "^OK$"); + public static readonly ATCommand SetProtocolAuto = new ATCommand("ATSP0", "^OK$"); + public static readonly ATCommand PrintVersion = new ATCommand("ATI", "^ELM327.*"); + public static readonly ATCommand CloseProtocol = new ATCommand("ATPC"); + + #endregion + + #region Properties & Fields + + public string Command { get; } + public string ExpectedResult { get; } + + #endregion + + #region Constructors + + private ATCommand(string command, string expectedResult = null) + { + this.Command = command; + this.ExpectedResult = expectedResult; + } + + #endregion + + #region Methods + + public override string ToString() + { + return Command; + } + + #endregion + + #region Operators + + public static implicit operator string(ATCommand command) + { + return command.ToString(); + } + + #endregion + } +} diff --git a/OBD.NET/OBD.NET/Communication/SerialConnection.cs b/OBD.NET/OBD.NET/Communication/SerialConnection.cs new file mode 100644 index 0000000..0894ecb --- /dev/null +++ b/OBD.NET/OBD.NET/Communication/SerialConnection.cs @@ -0,0 +1,56 @@ +using System; +using System.IO.Ports; + +namespace OBD.NET.Communication +{ + public class SerialConnection : IDisposable + { + #region Properties & Fields + + private readonly SerialPort _serialPort; + + public bool IsOpen => _serialPort.IsOpen; + + #endregion + + #region Constructors + + public SerialConnection(string port, int baudRate = 38400, Parity parity = Parity.None, + StopBits stopBits = StopBits.One, Handshake handshake = Handshake.None, int timeout = 5000) + { + _serialPort = new SerialPort(port, baudRate, parity) + { + StopBits = stopBits, + Handshake = handshake, + ReadTimeout = timeout, + WriteTimeout = timeout + }; + } + + #endregion + + #region Methods + + public void Connect() + { + _serialPort.Open(); + } + + public void Write(string text) + { + _serialPort.Write(text); + } + + public byte ReadByte() + { + return (byte)_serialPort.ReadByte(); + } + + public void Dispose() + { + _serialPort?.Dispose(); + } + + #endregion + } +} diff --git a/OBD.NET/OBD.NET/DataTypes/Count.cs b/OBD.NET/OBD.NET/DataTypes/Count.cs index 75b70c6..1c29a00 100644 --- a/OBD.NET/OBD.NET/DataTypes/Count.cs +++ b/OBD.NET/OBD.NET/DataTypes/Count.cs @@ -2,6 +2,12 @@ { public class Count : GenericData { + #region Properties & Fields + + protected override string Unit => null; + + #endregion + #region Constructors public Count(double value, double minValue, double maxValue) diff --git a/OBD.NET/OBD.NET/DataTypes/Degree.cs b/OBD.NET/OBD.NET/DataTypes/Degree.cs index a5de766..07afad0 100644 --- a/OBD.NET/OBD.NET/DataTypes/Degree.cs +++ b/OBD.NET/OBD.NET/DataTypes/Degree.cs @@ -2,6 +2,12 @@ { public class Degree : GenericData { + #region Properties & Fields + + protected override string Unit => "°"; + + #endregion + #region Constructors public Degree(double value, double minValue, double maxValue) diff --git a/OBD.NET/OBD.NET/DataTypes/DegreeCelsius.cs b/OBD.NET/OBD.NET/DataTypes/DegreeCelsius.cs index bed3565..f53f3aa 100644 --- a/OBD.NET/OBD.NET/DataTypes/DegreeCelsius.cs +++ b/OBD.NET/OBD.NET/DataTypes/DegreeCelsius.cs @@ -2,6 +2,12 @@ { public class DegreeCelsius : GenericData { + #region Properties & Fields + + protected override string Unit => "°C"; + + #endregion + #region Constructors public DegreeCelsius(double value, double minValue, double maxValue) diff --git a/OBD.NET/OBD.NET/DataTypes/GenericData.cs b/OBD.NET/OBD.NET/DataTypes/GenericData.cs index 3a7f764..fc25c75 100644 --- a/OBD.NET/OBD.NET/DataTypes/GenericData.cs +++ b/OBD.NET/OBD.NET/DataTypes/GenericData.cs @@ -2,7 +2,7 @@ namespace OBD.NET.DataTypes { - public class GenericData + public abstract class GenericData { #region Properties & Fields @@ -11,6 +11,8 @@ namespace OBD.NET.DataTypes public double MaxValue { get; } public bool IsFloatingPointValue { get; } + protected abstract string Unit { get; } + #endregion #region Constructors @@ -46,5 +48,14 @@ namespace OBD.NET.DataTypes } #endregion + + #region Methods + + public override string ToString() + { + return (IsFloatingPointValue ? Value.ToString("0.00") : Value.ToString()) + (Unit == null ? string.Empty : (" " + Unit)); + } + + #endregion } } diff --git a/OBD.NET/OBD.NET/DataTypes/GramPerSec.cs b/OBD.NET/OBD.NET/DataTypes/GramPerSec.cs index 37e3661..273a9d4 100644 --- a/OBD.NET/OBD.NET/DataTypes/GramPerSec.cs +++ b/OBD.NET/OBD.NET/DataTypes/GramPerSec.cs @@ -2,6 +2,12 @@ { public class GramPerSec : GenericData { + #region Properties & Fields + + protected override string Unit => "g/s"; + + #endregion + #region Constructors public GramPerSec(double value, double minValue, double maxValue) diff --git a/OBD.NET/OBD.NET/DataTypes/Kilometre.cs b/OBD.NET/OBD.NET/DataTypes/Kilometre.cs index 1683853..49069c5 100644 --- a/OBD.NET/OBD.NET/DataTypes/Kilometre.cs +++ b/OBD.NET/OBD.NET/DataTypes/Kilometre.cs @@ -2,6 +2,12 @@ { public class Kilometre : GenericData { + #region Properties & Fields + + protected override string Unit => "km"; + + #endregion + #region Constructors public Kilometre(double value, double minValue, double maxValue) diff --git a/OBD.NET/OBD.NET/DataTypes/KilometrePerHour.cs b/OBD.NET/OBD.NET/DataTypes/KilometrePerHour.cs index 5941a2f..14686cd 100644 --- a/OBD.NET/OBD.NET/DataTypes/KilometrePerHour.cs +++ b/OBD.NET/OBD.NET/DataTypes/KilometrePerHour.cs @@ -2,6 +2,12 @@ { public class KilometrePerHour : GenericData { + #region Properties & Fields + + protected override string Unit => "km/h"; + + #endregion + #region Constructors public KilometrePerHour(double value, double minValue, double maxValue) diff --git a/OBD.NET/OBD.NET/DataTypes/Kilopascal.cs b/OBD.NET/OBD.NET/DataTypes/Kilopascal.cs index dccf052..f30c733 100644 --- a/OBD.NET/OBD.NET/DataTypes/Kilopascal.cs +++ b/OBD.NET/OBD.NET/DataTypes/Kilopascal.cs @@ -2,6 +2,12 @@ { public class Kilopascal : GenericData { + #region Properties & Fields + + protected override string Unit => "kPa"; + + #endregion + #region Constructors public Kilopascal(double value, double minValue, double maxValue) diff --git a/OBD.NET/OBD.NET/DataTypes/LitresPerHour.cs b/OBD.NET/OBD.NET/DataTypes/LitresPerHour.cs index 3af5b13..e298429 100644 --- a/OBD.NET/OBD.NET/DataTypes/LitresPerHour.cs +++ b/OBD.NET/OBD.NET/DataTypes/LitresPerHour.cs @@ -2,6 +2,12 @@ { public class LitresPerHour : GenericData { + #region Properties & Fields + + protected override string Unit => "l/h"; + + #endregion + #region Constructors public LitresPerHour(double value, double minValue, double maxValue) diff --git a/OBD.NET/OBD.NET/DataTypes/Milliampere.cs b/OBD.NET/OBD.NET/DataTypes/Milliampere.cs index 0c93be2..ad2cf6f 100644 --- a/OBD.NET/OBD.NET/DataTypes/Milliampere.cs +++ b/OBD.NET/OBD.NET/DataTypes/Milliampere.cs @@ -2,6 +2,12 @@ { public class Milliampere : GenericData { + #region Properties & Fields + + protected override string Unit => "mA"; + + #endregion + #region Constructors public Milliampere(double value, double minValue, double maxValue) diff --git a/OBD.NET/OBD.NET/DataTypes/Minute.cs b/OBD.NET/OBD.NET/DataTypes/Minute.cs index cebbc8c..1c9cecd 100644 --- a/OBD.NET/OBD.NET/DataTypes/Minute.cs +++ b/OBD.NET/OBD.NET/DataTypes/Minute.cs @@ -2,6 +2,12 @@ { public class Minute : GenericData { + #region Properties & Fields + + protected override string Unit => "min"; + + #endregion + #region Constructors public Minute(double value, double minValue, double maxValue) diff --git a/OBD.NET/OBD.NET/DataTypes/NewtonMetre.cs b/OBD.NET/OBD.NET/DataTypes/NewtonMetre.cs index 680bc53..40dffcd 100644 --- a/OBD.NET/OBD.NET/DataTypes/NewtonMetre.cs +++ b/OBD.NET/OBD.NET/DataTypes/NewtonMetre.cs @@ -2,6 +2,12 @@ { public class NewtonMetre : GenericData { + #region Properties & Fields + + protected override string Unit => "N"; + + #endregion + #region Constructors public NewtonMetre(double value, double minValue, double maxValue) diff --git a/OBD.NET/OBD.NET/DataTypes/Pascal.cs b/OBD.NET/OBD.NET/DataTypes/Pascal.cs index c37095f..a0fc91d 100644 --- a/OBD.NET/OBD.NET/DataTypes/Pascal.cs +++ b/OBD.NET/OBD.NET/DataTypes/Pascal.cs @@ -2,6 +2,12 @@ { public class Pascal : GenericData { + #region Properties & Fields + + protected override string Unit => "Pa"; + + #endregion + #region Constructors public Pascal(double value, double minValue, double maxValue) diff --git a/OBD.NET/OBD.NET/DataTypes/Percent.cs b/OBD.NET/OBD.NET/DataTypes/Percent.cs index 6c07298..a255df9 100644 --- a/OBD.NET/OBD.NET/DataTypes/Percent.cs +++ b/OBD.NET/OBD.NET/DataTypes/Percent.cs @@ -2,6 +2,12 @@ { public class Percent : GenericData { + #region Properties & Fields + + protected override string Unit => "%"; + + #endregion + #region Constructors public Percent(double value, double minValue, double maxValue) diff --git a/OBD.NET/OBD.NET/DataTypes/Ratio.cs b/OBD.NET/OBD.NET/DataTypes/Ratio.cs index 9e85317..724c7e2 100644 --- a/OBD.NET/OBD.NET/DataTypes/Ratio.cs +++ b/OBD.NET/OBD.NET/DataTypes/Ratio.cs @@ -2,6 +2,12 @@ { public class Ratio : GenericData { + #region Properties & Fields + + protected override string Unit => null; + + #endregion + #region Constructors public Ratio(double value, double minValue, double maxValue) diff --git a/OBD.NET/OBD.NET/DataTypes/RevolutionsPerMinute.cs b/OBD.NET/OBD.NET/DataTypes/RevolutionsPerMinute.cs index 561d6a1..f8c9247 100644 --- a/OBD.NET/OBD.NET/DataTypes/RevolutionsPerMinute.cs +++ b/OBD.NET/OBD.NET/DataTypes/RevolutionsPerMinute.cs @@ -2,6 +2,12 @@ { public class RevolutionsPerMinute : GenericData { + #region Properties & Fields + + protected override string Unit => "rpm"; + + #endregion + #region Constructors public RevolutionsPerMinute(double value, double minValue, double maxValue) diff --git a/OBD.NET/OBD.NET/DataTypes/Second.cs b/OBD.NET/OBD.NET/DataTypes/Second.cs index ec15a44..ce37354 100644 --- a/OBD.NET/OBD.NET/DataTypes/Second.cs +++ b/OBD.NET/OBD.NET/DataTypes/Second.cs @@ -2,6 +2,12 @@ { public class Second : GenericData { + #region Properties & Fields + + protected override string Unit => "s"; + + #endregion + #region Constructors public Second(double value, double minValue, double maxValue) diff --git a/OBD.NET/OBD.NET/DataTypes/Volt.cs b/OBD.NET/OBD.NET/DataTypes/Volt.cs index 7669cf8..70dcdcc 100644 --- a/OBD.NET/OBD.NET/DataTypes/Volt.cs +++ b/OBD.NET/OBD.NET/DataTypes/Volt.cs @@ -2,6 +2,12 @@ { public class Volt : GenericData { + #region Properties & Fields + + protected override string Unit => "V"; + + #endregion + #region Constructors public Volt(double value, double minValue, double maxValue) diff --git a/OBD.NET/OBD.NET/Devices/ELM327.cs b/OBD.NET/OBD.NET/Devices/ELM327.cs new file mode 100644 index 0000000..de9bd75 --- /dev/null +++ b/OBD.NET/OBD.NET/Devices/ELM327.cs @@ -0,0 +1,196 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using OBD.NET.Commands; +using OBD.NET.Communication; +using OBD.NET.Enums; +using OBD.NET.Events; +using OBD.NET.Events.EventArgs; +using OBD.NET.Extensions; +using OBD.NET.Logging; +using OBD.NET.OBDData; + +namespace OBD.NET.Devices +{ + public class ELM327 : SerialDevice + { + #region Properties & Fields + + protected Dictionary _dataReceivedEventHandlers = new Dictionary(); + + protected static Dictionary PidCache { get; } = new Dictionary(); + protected static Dictionary DataTypeCache { get; } = new Dictionary(); + + protected Mode Mode { get; set; } = Mode.ShowCurrentData; //TODO DarthAffe 26.06.2016: Implement different modes + + protected override string ExpectedNoData => @"(NO DATA)|(SEARCHING)|(STOP)|\?"; + + #endregion + + #region Events + + public delegate void DataReceivedEventHandler(object sender, DataReceivedEventArgs args) where T : IOBDData; + + public delegate void RawDataReceivedEventHandler(object sender, RawDataReceivedEventArgs args); + public event RawDataReceivedEventHandler RawDataReceived; + + #endregion + + #region Constructors + + public ELM327(SerialConnection connection, IOBDLogger logger = null) + : base(connection, logger: logger) + { } + + #endregion + + #region Methods + + public override void Initialize() + { + base.Initialize(); + + Logger?.WriteLine("Initializing ...", OBDLogLevel.Debug); + int repeats = 3; + bool repeat = true; + while (repeat) + { + try + { + Logger?.WriteLine("Resetting Device ...", OBDLogLevel.Debug); + SendCommand(ATCommand.ResetDevice); + Thread.Sleep(1000); + + 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); + + repeat = false; + } + // DarthAffe 21.02.2017: This seems to happen sometimes, i don't know why - just retry. + catch (TimeoutException) + { + if (repeats > 0) + { + Logger?.WriteLine("Timout while initializing ... retry!", OBDLogLevel.Debug); + repeats--; + } + else + { + Logger?.WriteLine("Failed to initialize the device!", OBDLogLevel.Error); + throw; + } + } + } + + Thread.Sleep(1000); + } + + public virtual void SendCommand(ATCommand command) + { + SendCommand(command.Command, command.ExpectedResult); + } + + public virtual T RequestData() + where T : class, IOBDData, new() + { + Logger?.WriteLine("Requesting Type " + typeof(T).Name + " ...", OBDLogLevel.Debug); + + byte pid; + if (!PidCache.TryGetValue(typeof(T), out pid)) + { + T data = Activator.CreateInstance(); + pid = data.PID; + PidCache.Add(typeof(T), pid); + DataTypeCache.Add(pid, typeof(T)); + } + return RequestData(pid) as T; + } + + public virtual IOBDData RequestData(byte pid) + { + Logger?.WriteLine("Requesting PID " + pid.ToString("X2") + " ...", OBDLogLevel.Debug); + string result = SendCommand(((byte)Mode).ToString("X2") + pid.ToString("X2"), "4.*"); + Logger?.WriteLine("Result for PID " + pid.ToString("X2") + ": " + result, OBDLogLevel.Debug); + return ProcessData(result); + } + + protected virtual IOBDData ProcessData(string data) + { + if (data == null) return null; + + DateTime timestamp = DateTime.Now; + + RawDataReceived?.Invoke(this, new RawDataReceivedEventArgs(data, timestamp)); + + if (data.Length > 4) + if (data[0] == '4') + { + byte mode = (byte)data[1].GetHexVal(); + if (mode == (byte)Mode) + { + byte pid = (byte)data.Substring(2, 2).GetHexVal(); + Type dataType; + if (DataTypeCache.TryGetValue(pid, out dataType)) + { + IOBDData obdData = (IOBDData)Activator.CreateInstance(dataType); + obdData.Load(data.Substring(4, data.Length - 4)); + + IDataEventManager dataEventManager; + if (_dataReceivedEventHandlers.TryGetValue(dataType, out dataEventManager)) + dataEventManager.RaiseEvent(this, obdData, timestamp); + + return obdData; + } + } + } + + return null; + } + + public override void Dispose() + { + Dispose(true); + } + + public void Dispose(bool sendCloseProtocol) + { + if (sendCloseProtocol) + { + SendCommand(ATCommand.CloseProtocol); + Thread.Sleep(500); + } + + base.Dispose(); + } + + public void SubscribeDataReceived(DataReceivedEventHandler eventHandler) where T : IOBDData + { + IDataEventManager eventManager; + if (!_dataReceivedEventHandlers.TryGetValue(typeof(T), out eventManager)) + _dataReceivedEventHandlers.Add(typeof(T), (eventManager = new GenericDataEventManager())); + + ((GenericDataEventManager)eventManager).DataReceived += eventHandler; + } + + public void UnsubscribeDataReceived(DataReceivedEventHandler eventHandler) where T : IOBDData + { + IDataEventManager eventManager; + if (_dataReceivedEventHandlers.TryGetValue(typeof(T), out eventManager)) + ((GenericDataEventManager)eventManager).DataReceived -= eventHandler; + } + + #endregion + } +} diff --git a/OBD.NET/OBD.NET/Devices/STN1170.cs b/OBD.NET/OBD.NET/Devices/STN1170.cs new file mode 100644 index 0000000..8439124 --- /dev/null +++ b/OBD.NET/OBD.NET/Devices/STN1170.cs @@ -0,0 +1,18 @@ +using OBD.NET.Communication; +using OBD.NET.Logging; + +namespace OBD.NET.Devices +{ + public class STN1170 : ELM327 // Fully compatible device + { + //TODO DarthAffe 26.06.2016: Add ST-Commands and stuff + + #region Constructors + + public STN1170(SerialConnection connection, IOBDLogger logger = null) + : base(connection, logger) + { } + + #endregion + } +} diff --git a/OBD.NET/OBD.NET/Devices/SerialDevice.cs b/OBD.NET/OBD.NET/Devices/SerialDevice.cs new file mode 100644 index 0000000..29566e1 --- /dev/null +++ b/OBD.NET/OBD.NET/Devices/SerialDevice.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using OBD.NET.Communication; +using OBD.NET.Exceptions; +using OBD.NET.Logging; + +namespace OBD.NET.Devices +{ + public abstract class SerialDevice : IDisposable + { + #region Properties & Fields + + private readonly byte[] _responseBuffer = new byte[512]; + + protected IOBDLogger Logger { get; } + + protected SerialConnection Connection { get; } + protected char Prompt { get; set; } + protected char Terminator { get; set; } + + protected abstract string ExpectedNoData { get; } + + #endregion + + #region Constructors + + protected SerialDevice(SerialConnection connection, char prompt = '>', char terminator = '\r', IOBDLogger logger = null) + { + this.Connection = connection; + this.Prompt = prompt; + this.Terminator = terminator; + this.Logger = logger; + } + + #endregion + + #region Methods + + public virtual void Initialize() + { + Logger?.WriteLine("Opening Serial-Connection ...", OBDLogLevel.Debug); + Connection.Connect(); + + if (!Connection.IsOpen) + { + Logger?.WriteLine("Failed to open Serial-Connection.", OBDLogLevel.Error); + throw new SerialException("Failed to open Serial-Connection."); + } + else + Logger?.WriteLine("Opened Serial-Connection!", OBDLogLevel.Debug); + + Thread.Sleep(500); + } + + protected virtual string SendCommand(string command, string expectedResult = null) + { + if (!Connection.IsOpen) return null; + + command = PrepareCommand(command); + + Logger?.WriteLine("Writing Command: '" + command.Replace('\r', '\'') + "'", OBDLogLevel.Verbose); + Connection.Write(command); + + Logger?.WriteLine("Waiting for response ...", OBDLogLevel.Debug); + List results = ReadResponse(); + if (expectedResult == null) + return results.LastOrDefault(); + + // DarthAffe 22.02.2017: We expect the lats result to be the "best". + for (int i = results.Count - 1; i >= 0; i--) + if (Regex.Match(results[i], expectedResult, RegexOptions.IgnoreCase).Success) + return results[i]; + + if (results.Any(x => Regex.Match(x, ExpectedNoData, RegexOptions.IgnoreCase).Success)) + return null; + + Logger?.WriteLine("Unexpected Result: '" + string.Join("<\\r>", results) + "' expected was '" + expectedResult + "'", OBDLogLevel.Error); + throw new UnexpectedResultException(string.Join("<\\r>", results), expectedResult); + } + + protected virtual string PrepareCommand(string command) + { + if (command == null) throw new ArgumentNullException(nameof(command)); + + if (!command.EndsWith(Terminator.ToString(), StringComparison.Ordinal)) + command += Terminator; + + return command; + } + + protected virtual List ReadResponse() + { + byte b; + int count = 0; + do + { + b = Connection.ReadByte(); + if (b != 0x00) + _responseBuffer[count++] = b; + } while (b != Prompt); + + string response = Encoding.ASCII.GetString(_responseBuffer, 0, count - 1); // -1 to remove the prompt + Logger?.WriteLine("Response: '" + response.Replace("\r", "<\\r>").Replace("\n", "<\\n>") + "'", OBDLogLevel.Verbose); + return response.Split('\r').Select(x => x.Replace("\r", string.Empty).Replace("\n", string.Empty).Trim()).Where(x => !string.IsNullOrWhiteSpace(x)).ToList(); + } + + public virtual void Dispose() + { + Connection?.Dispose(); + } + + #endregion + } +} diff --git a/OBD.NET/OBD.NET/Enums/Mode.cs b/OBD.NET/OBD.NET/Enums/Mode.cs index 99bee8c..8722a8d 100644 --- a/OBD.NET/OBD.NET/Enums/Mode.cs +++ b/OBD.NET/OBD.NET/Enums/Mode.cs @@ -3,7 +3,7 @@ /// /// https://en.wikipedia.org/wiki/OBD-II_PIDs#Modes /// - internal enum Mode + public enum Mode { ShowCurrentData = 0x01, ShowFreezeFrameData = 0x02, diff --git a/OBD.NET/OBD.NET/Events/EventArgs/DataReceivedEventArgs.cs b/OBD.NET/OBD.NET/Events/EventArgs/DataReceivedEventArgs.cs new file mode 100644 index 0000000..4fada1f --- /dev/null +++ b/OBD.NET/OBD.NET/Events/EventArgs/DataReceivedEventArgs.cs @@ -0,0 +1,25 @@ +using System; +using OBD.NET.OBDData; + +namespace OBD.NET.Events.EventArgs +{ + public class DataReceivedEventArgs where T : IOBDData + { + #region Properties & Fields + + public T Data { get; } + public DateTime Timestamp { get; } + + #endregion + + #region Constructors + + public DataReceivedEventArgs(T data, DateTime timestamp) + { + this.Data = data; + this.Timestamp = timestamp; + } + + #endregion + } +} diff --git a/OBD.NET/OBD.NET/Events/EventArgs/RawDataReceivedEventArgs.cs b/OBD.NET/OBD.NET/Events/EventArgs/RawDataReceivedEventArgs.cs new file mode 100644 index 0000000..8fd7c0e --- /dev/null +++ b/OBD.NET/OBD.NET/Events/EventArgs/RawDataReceivedEventArgs.cs @@ -0,0 +1,24 @@ +using System; + +namespace OBD.NET.Events.EventArgs +{ + public class RawDataReceivedEventArgs + { + #region Properties & Fields + + public string Data { get; } + public DateTime Timestamp { get; } + + #endregion + + #region Constructors + + public RawDataReceivedEventArgs(string data, DateTime timestamp) + { + this.Data = data; + this.Timestamp = timestamp; + } + + #endregion + } +} diff --git a/OBD.NET/OBD.NET/Events/GenericDataEventManager.cs b/OBD.NET/OBD.NET/Events/GenericDataEventManager.cs new file mode 100644 index 0000000..2b355bf --- /dev/null +++ b/OBD.NET/OBD.NET/Events/GenericDataEventManager.cs @@ -0,0 +1,33 @@ +using System; +using OBD.NET.Devices; +using OBD.NET.Events.EventArgs; +using OBD.NET.OBDData; + +namespace OBD.NET.Events +{ + public class GenericDataEventManager : IDataEventManager where T : IOBDData + { + #region Properties & Fields + + #endregion + + #region Events + + internal event ELM327.DataReceivedEventHandler DataReceived; + + #endregion + + #region Constructors + + #endregion + + #region Methods + + public void RaiseEvent(object sender, IOBDData data, DateTime timestamp) + { + DataReceived?.Invoke(sender, new DataReceivedEventArgs((T)data, timestamp)); + } + + #endregion + } +} diff --git a/OBD.NET/OBD.NET/Events/IDataEventManager.cs b/OBD.NET/OBD.NET/Events/IDataEventManager.cs new file mode 100644 index 0000000..6ba08fd --- /dev/null +++ b/OBD.NET/OBD.NET/Events/IDataEventManager.cs @@ -0,0 +1,10 @@ +using System; +using OBD.NET.OBDData; + +namespace OBD.NET.Events +{ + public interface IDataEventManager + { + void RaiseEvent(object sender, IOBDData data, DateTime timestamp); + } +} diff --git a/OBD.NET/OBD.NET/Exceptions/SerialException.cs b/OBD.NET/OBD.NET/Exceptions/SerialException.cs new file mode 100644 index 0000000..74413e7 --- /dev/null +++ b/OBD.NET/OBD.NET/Exceptions/SerialException.cs @@ -0,0 +1,27 @@ +using System; +using System.Runtime.Serialization; + +namespace OBD.NET.Exceptions +{ + public class SerialException : Exception + { + #region Constructors + + public SerialException() + { } + + public SerialException(string message) + : base(message) + { } + + public SerialException(string message, Exception innerException) + : base(message, innerException) + { } + + protected SerialException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } + + #endregion + } +} diff --git a/OBD.NET/OBD.NET/Exceptions/UnexpectedResultException.cs b/OBD.NET/OBD.NET/Exceptions/UnexpectedResultException.cs new file mode 100644 index 0000000..d6b4f82 --- /dev/null +++ b/OBD.NET/OBD.NET/Exceptions/UnexpectedResultException.cs @@ -0,0 +1,47 @@ +using System; +using System.Runtime.Serialization; + +namespace OBD.NET.Exceptions +{ + public class UnexpectedResultException : Exception + { + #region Properties & Fields + + public string Result { get; } + public string ExpectedResult { get; } + + #endregion + + #region Constructors + + public UnexpectedResultException(string result, string expectedResult) + :this($"Unexpected result '{result}'. Expected was '{expectedResult}'", result, expectedResult) + { + this.Result = result; + this.ExpectedResult = expectedResult; + } + + public UnexpectedResultException(string message, string result, string expectedResult) + : base(message) + { + this.Result = result; + this.ExpectedResult = expectedResult; + } + + public UnexpectedResultException(string message, Exception innerException, string result, string expectedResult) + : base(message, innerException) + { + this.Result = result; + this.ExpectedResult = expectedResult; + } + + protected UnexpectedResultException(SerializationInfo info, StreamingContext context, string result, string expectedResult) + : base(info, context) + { + this.Result = result; + this.ExpectedResult = expectedResult; + } + + #endregion + } +} diff --git a/OBD.NET/OBD.NET/Extensions/HexExtension.cs b/OBD.NET/OBD.NET/Extensions/HexExtension.cs new file mode 100644 index 0000000..82dd90c --- /dev/null +++ b/OBD.NET/OBD.NET/Extensions/HexExtension.cs @@ -0,0 +1,24 @@ +using System; + +namespace OBD.NET.Extensions +{ + public static class HexExtension + { + public static int GetHexVal(this char hex) + { + return hex - (hex < 58 ? 48 : (hex < 97 ? 55 : 87)); + } + + public static int GetHexVal(this string hex) + { + if ((hex.Length % 2) == 1) + throw new ArgumentException("The binary key cannot have an odd number of digits"); + + int result = 0; + foreach (char c in hex) + result = (result << 4) + (GetHexVal(c)); + + return result; + } + } +} diff --git a/OBD.NET/OBD.NET/Logging/IOBDLogger.cs b/OBD.NET/OBD.NET/Logging/IOBDLogger.cs new file mode 100644 index 0000000..0d5aaf6 --- /dev/null +++ b/OBD.NET/OBD.NET/Logging/IOBDLogger.cs @@ -0,0 +1,7 @@ +namespace OBD.NET.Logging +{ + public interface IOBDLogger + { + void WriteLine(string text, OBDLogLevel level); + } +} diff --git a/OBD.NET/OBD.NET/Logging/OBDLogLevel.cs b/OBD.NET/OBD.NET/Logging/OBDLogLevel.cs new file mode 100644 index 0000000..e04d163 --- /dev/null +++ b/OBD.NET/OBD.NET/Logging/OBDLogLevel.cs @@ -0,0 +1,10 @@ +namespace OBD.NET.Logging +{ + public enum OBDLogLevel + { + None, + Error, + Verbose, + Debug + } +} diff --git a/OBD.NET/OBD.NET/OBD.NET.csproj b/OBD.NET/OBD.NET/OBD.NET.csproj index 0a9f742..8851d00 100644 --- a/OBD.NET/OBD.NET/OBD.NET.csproj +++ b/OBD.NET/OBD.NET/OBD.NET.csproj @@ -45,6 +45,8 @@ + + @@ -63,7 +65,19 @@ + + + + + + + + + + + + diff --git a/OBD.NET/OBD.NET/OBDData/AbstractOBDData.cs b/OBD.NET/OBD.NET/OBDData/AbstractOBDData.cs index 9880693..cded95d 100644 --- a/OBD.NET/OBD.NET/OBDData/AbstractOBDData.cs +++ b/OBD.NET/OBD.NET/OBDData/AbstractOBDData.cs @@ -1,5 +1,5 @@ using System; -using System.IO; +using OBD.NET.Extensions; namespace OBD.NET.OBDData { @@ -7,7 +7,7 @@ namespace OBD.NET.OBDData { #region Properties & Fields - public int PID { get; } + public byte PID { get; } private int _length; private byte[] _rawData; @@ -34,13 +34,13 @@ namespace OBD.NET.OBDData #region Constructors - public AbstractOBDData(int pid, int length) + public AbstractOBDData(byte pid, int length) { this.PID = pid; this._length = length; } - public AbstractOBDData(int pid, int length, byte[] rawData) + public AbstractOBDData(byte pid, int length, byte[] rawData) : this(pid, length) { this.RawData = rawData; @@ -50,13 +50,16 @@ namespace OBD.NET.OBDData #region Methods - public void Read(Stream stream) + public void Load(string data) { try { + if (((data.Length % 2) == 1) || ((data.Length / 2) != _length)) + throw new ArgumentException("The provided data is not valid", nameof(data)); + _rawData = new byte[_length]; - if (stream.Read(_rawData, 0, _length) != _length) - throw new InvalidDataException("Couldn't read enough bytes from the stream"); + for (int i = 0; i < _length; ++i) + _rawData[i] = (byte)((data[i << 1].GetHexVal() << 4) + (data[(i << 1) + 1].GetHexVal())); } catch { diff --git a/OBD.NET/OBD.NET/OBDData/IOBDData.cs b/OBD.NET/OBD.NET/OBDData/IOBDData.cs index 95afed5..64c59c6 100644 --- a/OBD.NET/OBD.NET/OBDData/IOBDData.cs +++ b/OBD.NET/OBD.NET/OBDData/IOBDData.cs @@ -1,11 +1,9 @@ -using System.IO; - -namespace OBD.NET.OBDData +namespace OBD.NET.OBDData { public interface IOBDData { - int PID { get; } + byte PID { get; } - void Read(Stream stream); + void Load(string data); } }