diff --git a/OBD.NET/OBD.NET.Common/Devices/CommandResult.cs b/OBD.NET/OBD.NET.Common/Devices/CommandResult.cs index 3c30514..c96e105 100644 --- a/OBD.NET/OBD.NET.Common/Devices/CommandResult.cs +++ b/OBD.NET/OBD.NET.Common/Devices/CommandResult.cs @@ -7,6 +7,7 @@ namespace OBD.NET.Common.Devices #region Properties & Fields public object Result { get; set; } + public string RawResult { get; set; } public AsyncManualResetEvent WaitHandle { get; } #endregion diff --git a/OBD.NET/OBD.NET.Common/Devices/ELM327.cs b/OBD.NET/OBD.NET.Common/Devices/ELM327.cs index 372d5ca..3e1d635 100644 --- a/OBD.NET/OBD.NET.Common/Devices/ELM327.cs +++ b/OBD.NET/OBD.NET.Common/Devices/ELM327.cs @@ -11,6 +11,7 @@ using OBD.NET.Common.Events.EventArgs; using OBD.NET.Common.Extensions; using OBD.NET.Common.Logging; using OBD.NET.Common.OBDData; +using OBD.NET.Common.OBDData.DTC; namespace OBD.NET.Common.Devices { @@ -147,33 +148,63 @@ namespace OBD.NET.Common.Devices return result.Result; } + public virtual TroubleCode[] RequestDTCs() => RequestDTCsAsync().Result; + + public virtual async Task RequestDTCsAsync() + { + Logger?.WriteLine("Requesting DTCs ...", OBDLogLevel.Debug); + MonitorStatusSinceDTCsClreared dtcStatus = await RequestDataAsync(); + + CommandResult result = SendCommand(((int)Mode.ShowStoredDiagnosticTroubleCodes).ToString()); + await result.WaitHandle.WaitAsync(); + + if (!(result.Result is List resultCodes)) return new TroubleCode[0]; + + int count = Math.Min(dtcStatus.DTCCount, resultCodes.Count); + return resultCodes.Take(count).ToArray(); + } + protected override object ProcessMessage(string message) { DateTime timestamp = DateTime.Now; RawDataReceived?.Invoke(this, new RawDataReceivedEventArgs(message, timestamp)); - if (message.Length > 4) + if (message.Length >= 4) { if (message[0] == '4') { byte mode = (byte)message[1].GetHexVal(); - if (mode == (byte)Mode) + switch ((Mode)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)); + case Mode.ShowCurrentData: + 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)); - if (DataReceivedEventHandlers.TryGetValue(dataType, out IDataEventManager dataEventManager)) - dataEventManager.RaiseEvent(this, obdData, timestamp); + if (DataReceivedEventHandlers.TryGetValue(dataType, out IDataEventManager dataEventManager)) + dataEventManager.RaiseEvent(this, obdData, timestamp); - if (DataReceivedEventHandlers.TryGetValue(typeof(IOBDData), out IDataEventManager genericDataEventManager)) - genericDataEventManager.RaiseEvent(this, obdData, timestamp); + if (DataReceivedEventHandlers.TryGetValue(typeof(IOBDData), out IDataEventManager genericDataEventManager)) + genericDataEventManager.RaiseEvent(this, obdData, timestamp); - return obdData; - } + return obdData; + } + break; + + case Mode.ShowStoredDiagnosticTroubleCodes: + //TODO DarthAffe 14.09.2017: I think multiple frames (as stated in the documentation for dtcs) aren't supported right now - how to handle them? + List troubleCodes = new List(); + for (int i = 2; (i + 3) < message.Length; i += 4) + { + string dtc = message.Substring(i, 4); + TroubleCode troubleCode = new TroubleCode(); + troubleCode.Load(dtc); + troubleCodes.Add(troubleCode); + } + return troubleCodes; } } } diff --git a/OBD.NET/OBD.NET.Common/Devices/SerialDevice.cs b/OBD.NET/OBD.NET.Common/Devices/SerialDevice.cs index 60a4520..f9032a8 100644 --- a/OBD.NET/OBD.NET.Common/Devices/SerialDevice.cs +++ b/OBD.NET/OBD.NET.Common/Devices/SerialDevice.cs @@ -183,6 +183,7 @@ namespace OBD.NET.Common.Devices { object data = ProcessMessage(message); CurrentCommand.CommandResult.Result = data; + CurrentCommand.CommandResult.RawResult = message; } /// diff --git a/OBD.NET/OBD.NET.Common/OBDData/00-1F/MonitorStatusSinceDTCsClreared.cs b/OBD.NET/OBD.NET.Common/OBDData/00-1F/MonitorStatusSinceDTCsClreared.cs new file mode 100644 index 0000000..4b611ad --- /dev/null +++ b/OBD.NET/OBD.NET.Common/OBDData/00-1F/MonitorStatusSinceDTCsClreared.cs @@ -0,0 +1,29 @@ +namespace OBD.NET.Common.OBDData +{ + public class MonitorStatusSinceDTCsClreared : AbstractOBDData + { + #region Properties & Fields + + public bool IsMilOn => (A & (1 << 7)) != 0; + + public int DTCCount => A & ~(1 << 7); + + //TODO DarthAffe 26.08.2017: Implement Test-Bits https://en.wikipedia.org/wiki/OBD-II_PIDs#Mode_1_PID_01 + + #endregion + + #region Constructors + + public MonitorStatusSinceDTCsClreared() + : base(0x01, 4) + { } + + #endregion + + #region Methods + + public override string ToString() => IsMilOn.ToString(); + + #endregion + } +} diff --git a/OBD.NET/OBD.NET.Common/OBDData/DTC/TroubleCode.cs b/OBD.NET/OBD.NET.Common/OBDData/DTC/TroubleCode.cs new file mode 100644 index 0000000..83ccfbe --- /dev/null +++ b/OBD.NET/OBD.NET.Common/OBDData/DTC/TroubleCode.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using OBD.NET.Common.Extensions; + +namespace OBD.NET.Common.OBDData.DTC +{ + public class TroubleCode : AbstractOBDData + { + #region Properties & Fields + + private static readonly Dictionary PART_DESCRIPTION = new Dictionary + { + + { TroublePart.Powertrain, "P" }, + { TroublePart.Chassis, "C" }, + { TroublePart.Body, "B" }, + { TroublePart.Network, "U" }, + }; + + public TroublePart TroublePart + { + get + { + byte data = (byte)(A & 0b11000000); + switch (data) + { + case 0: + return TroublePart.Powertrain; + case 0b01000000: + return TroublePart.Chassis; + case 0b10000000: + return TroublePart.Body; + case 0b11000000: + return TroublePart.Network; + default: + throw new IndexOutOfRangeException($"Can't parse TroublePart with data {data}"); + } + } + } + + public Definition Definition + { + get + { + byte data = (byte)(A & 0b00110000); + switch (data) + { + case 0: + return Definition.SAEJ2012; + case 0b01000000: + return Definition.Custom; + case 0b10000000: + return Definition.Undefined; + case 0b11000000: + return Definition.Undefined2; + default: + throw new IndexOutOfRangeException($"Can't parse Definition with data {data}"); + } + } + } + + public SpecificPart SpecificPart => (SpecificPart)(A & 0b00001111); + + public string FaultCode => B.ToHexString(); + + #endregion + + #region Constructors + + public TroubleCode() + : base(byte.MaxValue, 2) // DarthAffe 14.09.2017: Trouble-Codes don't have a pid + { } + + #endregion + + #region Methods + + public override string ToString() => $"{PART_DESCRIPTION[TroublePart]}{(int)Definition}{((byte)SpecificPart).ToHexString()[1]}{FaultCode}"; + + #endregion + } + + #region Data + + public enum TroublePart + { + Powertrain = 0, + Chassis = 1, + Body = 2, + Network = 3 + } + + public enum Definition + { + SAEJ2012 = 0, + Custom = 1, + Undefined = 2, + Undefined2 = 3 + } + + public enum SpecificPart + { + Undefined = 0, + FuelAndAirMetering = 1, + FuelAndAirMeteringInjectorCircuit = 2, + IgnitionSystemOfMisfire = 3, + AuxiliaryEmissionControls = 4, + VehicleSpeedControlAndIdleControlSystem = 5, + ComputerOutputCircuit = 6, + Transmission = 7, + Transmission2 = 8, + Undefined2 = 9, + Undefined3 = 10, + Undefined4 = 11, + Undefined5 = 12, + Undefined6 = 13, + Undefined7 = 14, + Undefined8 = 15, + } + + #endregion +}