initial commit
This commit is contained in:
42
Infrastructure/Kwp/ControllerIdent.cs
Normal file
42
Infrastructure/Kwp/ControllerIdent.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using HC_APTBS.Infrastructure.Kwp.Packets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace HC_APTBS.Infrastructure.Kwp
|
||||
{
|
||||
/// <summary>
|
||||
/// The info returned by the controller to a ReadIdent packet.
|
||||
/// </summary>
|
||||
public class ControllerIdent
|
||||
{
|
||||
public ControllerIdent(IEnumerable<Packet> packets)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (var packet in packets)
|
||||
{
|
||||
if (packet is AsciiDataPacket asciiPacket)
|
||||
{
|
||||
sb.Append(asciiPacket);
|
||||
}
|
||||
else if (packet is CodingWscPacket codingWscPacket)
|
||||
{
|
||||
sb.AppendLine();
|
||||
sb.Append(codingWscPacket);
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"ReadIdent returned packet of type {packet.GetType()}");
|
||||
}
|
||||
}
|
||||
Text = sb.ToString();
|
||||
}
|
||||
|
||||
public string Text { get; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Text;
|
||||
}
|
||||
}
|
||||
}
|
||||
53
Infrastructure/Kwp/ControllerInfo.cs
Normal file
53
Infrastructure/Kwp/ControllerInfo.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using HC_APTBS.Infrastructure.Kwp.Packets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace HC_APTBS.Infrastructure.Kwp
|
||||
{
|
||||
/// <summary>
|
||||
/// The info returned when a controller wakes up.
|
||||
/// </summary>
|
||||
public class ControllerInfo
|
||||
{
|
||||
public ControllerInfo(IEnumerable<Packet> packets)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (var packet in packets)
|
||||
{
|
||||
if (packet is AsciiDataPacket asciiPacket)
|
||||
{
|
||||
sb.Append(asciiPacket);
|
||||
if (asciiPacket.MoreDataAvailable)
|
||||
{
|
||||
MoreDataAvailable = true;
|
||||
}
|
||||
}
|
||||
else if (packet is CodingWscPacket codingPacket)
|
||||
{
|
||||
sb.Append($"{Environment.NewLine}{codingPacket}");
|
||||
SoftwareCoding = codingPacket.SoftwareCoding;
|
||||
WorkshopCode = codingPacket.WorkshopCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Controller wakeup returned packet of type {packet.GetType()}");
|
||||
}
|
||||
}
|
||||
Text = sb.ToString();
|
||||
}
|
||||
|
||||
public string Text { get; }
|
||||
|
||||
public bool MoreDataAvailable { get; }
|
||||
|
||||
public int SoftwareCoding { get; }
|
||||
|
||||
public int WorkshopCode { get; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Text;
|
||||
}
|
||||
}
|
||||
}
|
||||
868
Infrastructure/Kwp/FtdiInterface.cs
Normal file
868
Infrastructure/Kwp/FtdiInterface.cs
Normal file
@@ -0,0 +1,868 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace HC_APTBS.Infrastructure.Kwp
|
||||
{
|
||||
|
||||
public class FT_DEVICE_INFO_NODE
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates device state. Can be any combination of the following: FT_FLAGS_OPENED, FT_FLAGS_HISPEED
|
||||
/// </summary>
|
||||
public UInt32 Flags;
|
||||
/// <summary>
|
||||
/// Indicates the device type. Can be one of the following: FT_DEVICE_232R, FT_DEVICE_2232C, FT_DEVICE_BM, FT_DEVICE_AM, FT_DEVICE_100AX or FT_DEVICE_UNKNOWN
|
||||
/// </summary>
|
||||
public byte Type;
|
||||
/// <summary>
|
||||
/// The Vendor ID and Product ID of the device
|
||||
/// </summary>
|
||||
public UInt32 ID;
|
||||
/// <summary>
|
||||
/// The physical location identifier of the device
|
||||
/// </summary>
|
||||
public UInt32 LocId;
|
||||
/// <summary>
|
||||
/// The device serial number
|
||||
/// </summary>
|
||||
public string SerialNumber;
|
||||
/// <summary>
|
||||
/// The device description
|
||||
/// </summary>
|
||||
public string Description;
|
||||
/// <summary>
|
||||
/// The device handle. This value is not used externally and is provided for information only.
|
||||
/// If the device is not open, this value is 0.
|
||||
/// </summary>
|
||||
public IntPtr ftHandle;
|
||||
}
|
||||
|
||||
public class FtdiInterface : IInterface
|
||||
{
|
||||
private FT _ft = null;
|
||||
private IntPtr _handle = IntPtr.Zero;
|
||||
private readonly byte[] _buf = new byte[1];
|
||||
|
||||
public FtdiInterface(string serialNumber, int baudRate)
|
||||
{
|
||||
_ft = new FT();
|
||||
|
||||
var status = _ft.Open(
|
||||
serialNumber, FT.OpenExFlags.BySerialNumber, out _handle);
|
||||
FT.AssertOk(status);
|
||||
|
||||
status = _ft.SetBaudRate(_handle, (uint)baudRate);
|
||||
FT.AssertOk(status);
|
||||
|
||||
status = _ft.SetDataCharacteristics(
|
||||
_handle,
|
||||
FT.Bits.Eight,
|
||||
FT.StopBits.One,
|
||||
FT.Parity.None);
|
||||
FT.AssertOk(status);
|
||||
|
||||
status = _ft.SetFlowControl(
|
||||
_handle,
|
||||
FT.FlowControl.None, 0, 0);
|
||||
FT.AssertOk(status);
|
||||
|
||||
status = _ft.ClrRts(_handle);
|
||||
FT.AssertOk(status);
|
||||
|
||||
status = _ft.SetDtr(_handle);
|
||||
FT.AssertOk(status);
|
||||
|
||||
status = _ft.SetTimeouts(
|
||||
_handle,
|
||||
500,
|
||||
500);
|
||||
FT.AssertOk(status);
|
||||
|
||||
// Should allow faster response times for small packets
|
||||
status = _ft.SetLatencyTimer(_handle, 1);
|
||||
FT.AssertOk(status);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_handle != IntPtr.Zero)
|
||||
{
|
||||
var status = _ft.Close(_handle);
|
||||
_handle = IntPtr.Zero;
|
||||
FT.AssertOk(status);
|
||||
}
|
||||
|
||||
if (_ft != null)
|
||||
{
|
||||
_ft.Dispose();
|
||||
_ft = null;
|
||||
}
|
||||
}
|
||||
|
||||
public byte ReadByte()
|
||||
{
|
||||
var status = _ft.Read(_handle, _buf, 1, out uint countOfBytesRead);
|
||||
FT.AssertOk(status);
|
||||
if (countOfBytesRead != 1)
|
||||
{
|
||||
throw new TimeoutException("Read timed out");
|
||||
}
|
||||
|
||||
var b = _buf[0];
|
||||
return b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a byte to the interface but do not read/discard its echo.
|
||||
/// </summary>
|
||||
public void WriteByteRaw(byte b)
|
||||
{
|
||||
_buf[0] = b;
|
||||
var status = _ft.Write(_handle, _buf, 1, out uint countOfBytesWritten);
|
||||
FT.AssertOk(status);
|
||||
if (countOfBytesWritten != 1)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Expected to write 1 byte but wrote {countOfBytesWritten} bytes");
|
||||
}
|
||||
}
|
||||
|
||||
public void SetBreakOn()
|
||||
{
|
||||
var status = _ft.SetBreakOn(_handle);
|
||||
FT.AssertOk(status);
|
||||
}
|
||||
|
||||
public void SetBreakOff()
|
||||
{
|
||||
var status = _ft.SetBreakOff(_handle);
|
||||
FT.AssertOk(status);
|
||||
}
|
||||
|
||||
public void ClearReceiveBuffer()
|
||||
{
|
||||
var status = _ft.Purge(_handle, FT.PurgeMask.RX);
|
||||
FT.AssertOk(status);
|
||||
}
|
||||
|
||||
public static uint GetDevicesCount()
|
||||
{
|
||||
uint numdevs = 0;
|
||||
FT tmp_ft = new FT();
|
||||
try
|
||||
{
|
||||
var status = tmp_ft.CreateDeviceInfoList(out numdevs);
|
||||
FT.AssertOk(status);
|
||||
}
|
||||
finally
|
||||
{
|
||||
tmp_ft.Dispose();
|
||||
}
|
||||
|
||||
return numdevs;
|
||||
}
|
||||
|
||||
public static void GetDeviceList(FT_DEVICE_INFO_NODE[] devicelist)
|
||||
{
|
||||
FT tmp_ft = new FT();
|
||||
try
|
||||
{
|
||||
uint numdevs;
|
||||
var status = tmp_ft.CreateDeviceInfoList(out numdevs);
|
||||
FT.AssertOk(status);
|
||||
byte[] sernum = new byte[16];
|
||||
byte[] desc = new byte[64];
|
||||
for (UInt32 i = 0; i < numdevs; i++)
|
||||
{
|
||||
devicelist[i] = new FT_DEVICE_INFO_NODE();
|
||||
var ftStatus = tmp_ft.GetDeviceInfoDetail(i, out devicelist[i].Flags, out devicelist[i].Type, out devicelist[i].ID, out devicelist[i].LocId, sernum, desc, out devicelist[i].ftHandle);
|
||||
devicelist[i].SerialNumber = Encoding.ASCII.GetString(sernum);
|
||||
devicelist[i].Description = Encoding.ASCII.GetString(desc);
|
||||
// Trim strings to first occurrence of a null terminator character
|
||||
int nullIndex = devicelist[i].SerialNumber.IndexOf("\0", StringComparison.Ordinal);
|
||||
if (nullIndex != -1)
|
||||
devicelist[i].SerialNumber = devicelist[i].SerialNumber.Substring(0, nullIndex);
|
||||
nullIndex = devicelist[i].Description.IndexOf("\0");
|
||||
if (nullIndex != -1)
|
||||
devicelist[i].Description = devicelist[i].Description.Substring(0, nullIndex);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
tmp_ft.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public byte ReadEEPROM(uint Address, ref ushort EEValue)
|
||||
{
|
||||
var status = _ft.ReadEEPROM(_handle, Address, ref EEValue);
|
||||
FT.AssertOk(status);
|
||||
return (byte) status;
|
||||
}
|
||||
|
||||
private static byte DecodeByte(byte b)
|
||||
{
|
||||
byte tmp = (byte)(((((b & 2) << 1) | (b & 0xF8)) << 3) | (b & 1));
|
||||
byte tmp2 = (byte)((((((b >> 2) & 0x10) | (b & 0x87)) >> 1) | (b & 0x30)) >> 1);
|
||||
return (byte)((tmp << 1) | tmp2); ;
|
||||
}
|
||||
|
||||
private static UInt32 DecodeFTChipID(UInt32 id)
|
||||
{
|
||||
return (UInt32)((DecodeByte((byte)(id & 0xFF)) << 24) | (DecodeByte((byte)(id >> 8)) << 16) | (DecodeByte((byte)(id >> 16)) << 8) | DecodeByte((byte)((id >> 24) & 0xFF))) ^ 0xA5F0F7D1;
|
||||
}
|
||||
|
||||
public static UInt32 CalculateKey(UInt32 id)
|
||||
{
|
||||
UInt32 result = 0;
|
||||
result = id;
|
||||
for (UInt32 i = 0; i < 10; i++)
|
||||
{
|
||||
if ((result & 0x00000001) == 0x00000001)
|
||||
{
|
||||
result = (result >> 1) | 0x80000000;
|
||||
result ^= 0x63AC294D;
|
||||
}
|
||||
else
|
||||
{
|
||||
result >>= 1;
|
||||
result ^= 0xF19A5712;
|
||||
}
|
||||
if ((result + 0x4A2FDC49) > 0xFFFFFFFF)
|
||||
{
|
||||
result += 0x4A2FDC49;
|
||||
if ((result & 0x00000001) == 0x00000001)
|
||||
{
|
||||
result = (result >> 1) | 0x80000000;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = (result >> 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result += 0x4A2FDC49;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public bool ReadChipID(ref UInt32 ChipID)
|
||||
{
|
||||
ushort a = 0, b = 0;
|
||||
var status = _ft.ReadEEPROM(_handle, 0x44, ref a);
|
||||
FT.AssertOk(status);
|
||||
status = _ft.ReadEEPROM(_handle, 0x43, ref b);
|
||||
FT.AssertOk(status);
|
||||
ChipID = DecodeFTChipID( (UInt32) (a << 16 | b) );
|
||||
return (status == FT.Status.Ok);
|
||||
}
|
||||
|
||||
public bool CheckIsChipIDVaild(UInt32 Key)
|
||||
{
|
||||
UInt32 ChipID = 0;
|
||||
if (!ReadChipID(ref ChipID))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
UInt32 CorrectKey = CalculateKey(ChipID);
|
||||
return (Key == CorrectKey);
|
||||
}
|
||||
|
||||
public bool EEUserAreaSize(ref uint UASize)
|
||||
{
|
||||
var status = _ft.EE_UASize(_handle, ref UASize);
|
||||
FT.AssertOk(status);
|
||||
return (status == FT.Status.Ok);
|
||||
}
|
||||
|
||||
public bool EEReadUserArea(byte[] UserAreaDataBuffer, ref uint numBytesRead)
|
||||
{
|
||||
var status = _ft.EE_UARead(_handle, UserAreaDataBuffer, UserAreaDataBuffer.Length, ref numBytesRead);
|
||||
FT.AssertOk(status);
|
||||
return (status == FT.Status.Ok);
|
||||
}
|
||||
|
||||
public bool EEWriteUserArea(byte[] UserAreaDataBuffer)
|
||||
{
|
||||
var status = _ft.EE_UAWrite(_handle, UserAreaDataBuffer, UserAreaDataBuffer.Length);
|
||||
FT.AssertOk(status);
|
||||
return (status == FT.Status.Ok);
|
||||
}
|
||||
|
||||
public bool CheckCableValidity()
|
||||
{
|
||||
|
||||
UInt32 user_area_size = 0;
|
||||
if (!this.EEUserAreaSize(ref user_area_size) || (user_area_size == 0))
|
||||
{
|
||||
throw new InvalidOperationException($"Cable validation error");
|
||||
}
|
||||
byte[] user_area_data = new byte[user_area_size];
|
||||
UInt32 bytes_read = 0;
|
||||
if (!this.EEReadUserArea(user_area_data, ref bytes_read) || (bytes_read == 0))
|
||||
{
|
||||
throw new InvalidOperationException($"Cable validation error");
|
||||
}
|
||||
UInt32 Key = (UInt32)((user_area_data[0] << 24) | (user_area_data[1] << 16) | (user_area_data[2] << 8) | user_area_data[3]);
|
||||
if (!this.CheckIsChipIDVaild(Key))
|
||||
{
|
||||
throw new InvalidOperationException($"Cable validation error");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[System.Reflection.ObfuscationAttribute(Feature = "renaming")] //cuidao
|
||||
class FT : IDisposable
|
||||
{
|
||||
private IntPtr _d2xx = IntPtr.Zero;
|
||||
|
||||
// Delegates used to call into the FTID D2xx DLL
|
||||
#pragma warning disable CS0649
|
||||
private readonly FTDll.SetVidPid _setVidPid;
|
||||
private readonly FTDll.OpenBySerialNumber _openBySerialNumber;
|
||||
private readonly FTDll.Close _close;
|
||||
private readonly FTDll.SetBaudRate _setBaudRate;
|
||||
private readonly FTDll.SetDataCharacteristics _setDataCharacteristics;
|
||||
private readonly FTDll.SetFlowControl _setFlowControl;
|
||||
private readonly FTDll.SetDtr _setDtr;
|
||||
private readonly FTDll.ClrDtr _clrDtr;
|
||||
private readonly FTDll.SetRts _setRts;
|
||||
private readonly FTDll.ClrRts _clrRts;
|
||||
private readonly FTDll.SetTimeouts _setTimeouts;
|
||||
private readonly FTDll.SetLatencyTimer _setLatencyTimer;
|
||||
private readonly FTDll.Purge _purge;
|
||||
private readonly FTDll.SetBreakOn _setBreakOn;
|
||||
private readonly FTDll.SetBreakOff _setBreakOff;
|
||||
private readonly FTDll.Read _read;
|
||||
private readonly FTDll.Write _write;
|
||||
private readonly FTDll.CreateDeviceInfoList _createDeviceInfoList;
|
||||
private readonly FTDll.GetDeviceInfoDetail _getDeviceInfoDetail;
|
||||
private readonly FTDll.ReadEE _readEE;
|
||||
private readonly FTDll.EE_UASize _ee_UASize;
|
||||
private readonly FTDll.EE_UARead _ee_UARead;
|
||||
private readonly FTDll.EE_UAWrite _ee_UAWrite;
|
||||
#pragma warning restore CS0649
|
||||
|
||||
//[System.Reflection.ObfuscationAttribute(Feature = "renaming")] //no funciona imposible
|
||||
public FT()
|
||||
{
|
||||
string libName;
|
||||
bool isMacOs = false;
|
||||
bool isLinux = false;
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
libName = "libftd2xx.dylib";
|
||||
isMacOs = true;
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
libName = "ftd2xx.so";
|
||||
isLinux = true;
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
//libName = Environment.Is64BitProcess ? "ftd2xx64.dll" : "ftd2xx.dll";
|
||||
libName = "ftd2xx.dll";
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Unknown OS: {RuntimeInformation.OSDescription}");
|
||||
}
|
||||
|
||||
//_d2xx = NativeLibrary.Load(
|
||||
// libName, typeof(FT).Assembly, DllImportSearchPath.SafeDirectories);
|
||||
_d2xx = LoadLibrary(libName);
|
||||
if (_d2xx == IntPtr.Zero)
|
||||
{
|
||||
_d2xx = LoadLibrary(@Path.GetDirectoryName(GetType().Assembly.Location) + "\\"+ libName);
|
||||
}
|
||||
|
||||
if (_d2xx == IntPtr.Zero)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to load FTD2XX.DLL");
|
||||
}
|
||||
|
||||
var fieldNames = new List<string>
|
||||
{
|
||||
nameof(_openBySerialNumber),
|
||||
nameof(_close),
|
||||
nameof(_setBaudRate),
|
||||
nameof(_setDataCharacteristics),
|
||||
nameof(_setFlowControl),
|
||||
nameof(_setDtr),
|
||||
nameof(_clrDtr),
|
||||
nameof(_setRts),
|
||||
nameof(_clrRts),
|
||||
nameof(_setTimeouts),
|
||||
nameof(_setLatencyTimer),
|
||||
nameof(_purge),
|
||||
nameof(_setBreakOn),
|
||||
nameof(_setBreakOff),
|
||||
nameof(_read),
|
||||
nameof(_write),
|
||||
nameof(_createDeviceInfoList),
|
||||
nameof(_getDeviceInfoDetail),
|
||||
nameof(_readEE),
|
||||
nameof(_ee_UASize),
|
||||
nameof(_ee_UARead),
|
||||
nameof(_ee_UAWrite)
|
||||
};
|
||||
if (isMacOs || isLinux)
|
||||
{
|
||||
fieldNames.Add(nameof(_setVidPid));
|
||||
}
|
||||
|
||||
foreach (var fieldName in fieldNames)
|
||||
{
|
||||
var fieldInfo = typeof(FT).GetField(
|
||||
fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
var nativeMethodName = fieldInfo.FieldType.GetCustomAttribute<SymbolNameAttribute>().Name;
|
||||
//var export = NativeLibrary.GetExport(_d2xx, nativeMethodName);
|
||||
var export = GetProcAddress(_d2xx, nativeMethodName);
|
||||
var delegateVal = Marshal.GetDelegateForFunctionPointer(export, fieldInfo.FieldType);
|
||||
fieldInfo.SetValue(this, delegateVal);
|
||||
}
|
||||
|
||||
if (isMacOs || isLinux)
|
||||
{
|
||||
var vidStr = Environment.GetEnvironmentVariable("FTDI_VID");
|
||||
var pidStr = Environment.GetEnvironmentVariable("FTDI_PID");
|
||||
if (!string.IsNullOrEmpty(vidStr) && !string.IsNullOrEmpty(pidStr))
|
||||
{
|
||||
//var vid = Utils.ParseUint(vidStr);
|
||||
var vid = UInt32.Parse(vidStr);
|
||||
var pid = UInt32.Parse(pidStr);
|
||||
//var pid = Utils.ParseUint(pidStr);
|
||||
System.Diagnostics.Debug.WriteLine($"Setting FTDI VID=0x{vid:X4}, PID=0x{pid:X4}");
|
||||
var status = SetVidPid(vid, pid);
|
||||
AssertOk(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_d2xx != IntPtr.Zero)
|
||||
{
|
||||
//NativeLibrary.Free(_d2xx);
|
||||
FreeLibrary(_d2xx);
|
||||
_d2xx = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
public static void AssertOk(FT.Status status)
|
||||
{
|
||||
if (status != FT.Status.Ok)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"D2xx library returned {status} instead of Ok");
|
||||
}
|
||||
}
|
||||
|
||||
public Status SetVidPid(
|
||||
uint vid,
|
||||
uint pid)
|
||||
{
|
||||
return _setVidPid(vid, pid);
|
||||
}
|
||||
|
||||
public Status Open(
|
||||
string serialNumber,
|
||||
OpenExFlags flags,
|
||||
out IntPtr handle)
|
||||
{
|
||||
return _openBySerialNumber(serialNumber, flags, out handle);
|
||||
}
|
||||
|
||||
public Status Close(
|
||||
IntPtr handle)
|
||||
{
|
||||
return _close(handle);
|
||||
}
|
||||
|
||||
public Status SetBaudRate(
|
||||
IntPtr handle,
|
||||
uint baudRate)
|
||||
{
|
||||
return _setBaudRate(handle, baudRate);
|
||||
}
|
||||
|
||||
public Status SetDataCharacteristics(
|
||||
IntPtr handle,
|
||||
Bits wordLength,
|
||||
StopBits stopBits,
|
||||
Parity parity)
|
||||
{
|
||||
return _setDataCharacteristics(handle, wordLength, stopBits, parity);
|
||||
}
|
||||
|
||||
public Status SetFlowControl(
|
||||
IntPtr handle,
|
||||
FlowControl flowControl,
|
||||
byte xonChar,
|
||||
byte xoffChar)
|
||||
{
|
||||
return _setFlowControl(handle, flowControl, xonChar, xoffChar);
|
||||
}
|
||||
|
||||
public Status SetDtr(
|
||||
IntPtr handle)
|
||||
{
|
||||
return _setDtr(handle);
|
||||
}
|
||||
|
||||
public Status ClrDtr(
|
||||
IntPtr handle)
|
||||
{
|
||||
return _clrDtr(handle);
|
||||
}
|
||||
|
||||
public Status SetRts(
|
||||
IntPtr handle)
|
||||
{
|
||||
return _setRts(handle);
|
||||
}
|
||||
|
||||
public Status ClrRts(
|
||||
IntPtr handle)
|
||||
{
|
||||
return _clrRts(handle);
|
||||
}
|
||||
|
||||
public Status SetTimeouts(
|
||||
IntPtr handle,
|
||||
uint readTimeoutMS,
|
||||
uint writeTimeoutMS)
|
||||
{
|
||||
return _setTimeouts(handle, readTimeoutMS, writeTimeoutMS);
|
||||
}
|
||||
|
||||
public Status SetLatencyTimer(
|
||||
IntPtr handle,
|
||||
byte timerMS)
|
||||
{
|
||||
return _setLatencyTimer(handle, timerMS);
|
||||
}
|
||||
|
||||
public Status Purge(
|
||||
IntPtr handle,
|
||||
PurgeMask mask)
|
||||
{
|
||||
return _purge(handle, mask);
|
||||
}
|
||||
|
||||
public Status SetBreakOn(
|
||||
IntPtr handle)
|
||||
{
|
||||
return _setBreakOn(handle);
|
||||
}
|
||||
|
||||
public Status SetBreakOff(
|
||||
IntPtr handle)
|
||||
{
|
||||
return _setBreakOff(handle);
|
||||
}
|
||||
|
||||
public Status Read(
|
||||
IntPtr handle,
|
||||
byte[] buffer,
|
||||
uint countOfBytesToRead,
|
||||
out uint countOfBytesRead)
|
||||
{
|
||||
return _read(handle, buffer, countOfBytesToRead, out countOfBytesRead);
|
||||
}
|
||||
|
||||
public Status Write(
|
||||
IntPtr handle,
|
||||
byte[] buffer,
|
||||
uint countOfBytesToWrite,
|
||||
out uint countOfBytesWritten)
|
||||
{
|
||||
return _write(handle, buffer, countOfBytesToWrite, out countOfBytesWritten);
|
||||
}
|
||||
|
||||
public Status CreateDeviceInfoList(out uint devcount)
|
||||
{
|
||||
return _createDeviceInfoList(out devcount);
|
||||
}
|
||||
|
||||
public Status GetDeviceInfoDetail(
|
||||
UInt32 index,
|
||||
out UInt32 flags,
|
||||
out byte chiptype,
|
||||
out UInt32 id,
|
||||
out UInt32 locid,
|
||||
byte[] serialnumber,
|
||||
byte[] description,
|
||||
out IntPtr ftHandle)
|
||||
{
|
||||
return _getDeviceInfoDetail(index, out flags, out chiptype, out id, out locid, serialnumber, description, out ftHandle);
|
||||
}
|
||||
|
||||
public Status ReadEEPROM(
|
||||
IntPtr ftHandle,
|
||||
uint dwWordOffset,
|
||||
ref ushort lpwValue)
|
||||
{
|
||||
return _readEE(ftHandle, dwWordOffset, ref lpwValue);
|
||||
}
|
||||
|
||||
public Status EE_UASize(
|
||||
IntPtr ftHandle,
|
||||
ref uint dwSize)
|
||||
{
|
||||
return _ee_UASize(ftHandle, ref dwSize);
|
||||
}
|
||||
|
||||
public Status EE_UARead(
|
||||
IntPtr ftHandle,
|
||||
byte[] pucData,
|
||||
int dwDataLen,
|
||||
ref uint lpdwDataRead)
|
||||
{
|
||||
return _ee_UARead(ftHandle, pucData, dwDataLen, ref lpdwDataRead);
|
||||
}
|
||||
|
||||
public Status EE_UAWrite(
|
||||
IntPtr ftHandle,
|
||||
byte[] pucData,
|
||||
int dwDataLen)
|
||||
{
|
||||
return _ee_UAWrite(ftHandle, pucData, dwDataLen);
|
||||
}
|
||||
|
||||
public enum Status : uint
|
||||
{
|
||||
Ok = 0,
|
||||
InvalidHandle,
|
||||
DeviceNotFound,
|
||||
DeviceNotOpened,
|
||||
IOError,
|
||||
insufficient_resources,
|
||||
InvalidParameter,
|
||||
InvalidBaudRate,
|
||||
DeviceNotOpenedForErase,
|
||||
DeviceNotOpenedForWrite,
|
||||
FailedToWriteDevice,
|
||||
EepromReadFailed,
|
||||
EepromWriteFailed,
|
||||
EepromEraseFailed,
|
||||
EepromNotPresent,
|
||||
EepromNotProgrammed,
|
||||
InvalidArgs,
|
||||
NotSupported,
|
||||
OtherError,
|
||||
DeviceListNotReady,
|
||||
};
|
||||
|
||||
[Flags]
|
||||
public enum OpenExFlags : uint
|
||||
{
|
||||
BySerialNumber = 1,
|
||||
ByDescription = 2,
|
||||
ByLocation = 4
|
||||
};
|
||||
|
||||
public enum Bits : byte
|
||||
{
|
||||
Eight = 8,
|
||||
Seven = 7
|
||||
};
|
||||
|
||||
public enum StopBits : byte
|
||||
{
|
||||
One = 0,
|
||||
Two = 2
|
||||
};
|
||||
|
||||
public enum Parity : byte
|
||||
{
|
||||
None = 0,
|
||||
Odd = 1,
|
||||
Even = 2,
|
||||
Mark = 3,
|
||||
Space = 4
|
||||
};
|
||||
|
||||
public enum FlowControl : ushort
|
||||
{
|
||||
None = 0x0000,
|
||||
RtsCts = 0x0100,
|
||||
DtrDsr = 0x0200,
|
||||
XonXoff = 0x0400
|
||||
};
|
||||
|
||||
[Flags]
|
||||
public enum PurgeMask : uint
|
||||
{
|
||||
RX = 1,
|
||||
TX = 2
|
||||
};
|
||||
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern IntPtr LoadLibrary(string dllToLoad);
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern bool FreeLibrary(IntPtr hModule);
|
||||
|
||||
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Delegate)]
|
||||
public class SymbolNameAttribute : Attribute
|
||||
{
|
||||
public SymbolNameAttribute(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
public string Name { get; private set; }
|
||||
}
|
||||
|
||||
static class FTDll
|
||||
{
|
||||
[SymbolName("FT_SetVIDPID")]
|
||||
public delegate FT.Status SetVidPid(
|
||||
uint vid, uint pid);
|
||||
|
||||
[SymbolName("FT_OpenEx")]
|
||||
public delegate FT.Status OpenBySerialNumber(
|
||||
[MarshalAs(UnmanagedType.LPStr)] string serialNumber,
|
||||
FT.OpenExFlags flags,
|
||||
out IntPtr handle);
|
||||
|
||||
[SymbolName("FT_Close")]
|
||||
public delegate FT.Status Close(
|
||||
IntPtr handle);
|
||||
|
||||
[SymbolName("FT_SetBaudRate")]
|
||||
public delegate FT.Status SetBaudRate(
|
||||
IntPtr handle,
|
||||
uint baudRate);
|
||||
|
||||
[SymbolName("FT_SetDataCharacteristics")]
|
||||
public delegate FT.Status SetDataCharacteristics(
|
||||
IntPtr handle,
|
||||
FT.Bits wordLength,
|
||||
FT.StopBits stopBits,
|
||||
FT.Parity parity);
|
||||
|
||||
[SymbolName("FT_SetFlowControl")]
|
||||
public delegate FT.Status SetFlowControl(
|
||||
IntPtr handle,
|
||||
FT.FlowControl flowControl,
|
||||
byte xonChar,
|
||||
byte xoffChar);
|
||||
|
||||
[SymbolName("FT_SetDtr")]
|
||||
public delegate FT.Status SetDtr(
|
||||
IntPtr handle);
|
||||
|
||||
[SymbolName("FT_ClrDtr")]
|
||||
public delegate FT.Status ClrDtr(
|
||||
IntPtr handle);
|
||||
|
||||
[SymbolName("FT_SetRts")]
|
||||
public delegate FT.Status SetRts(
|
||||
IntPtr handle);
|
||||
|
||||
[SymbolName("FT_ClrRts")]
|
||||
public delegate FT.Status ClrRts(
|
||||
IntPtr handle);
|
||||
|
||||
[SymbolName("FT_SetTimeouts")]
|
||||
public delegate FT.Status SetTimeouts(
|
||||
IntPtr handle,
|
||||
uint readTimeoutMS,
|
||||
uint writeTimeoutMS);
|
||||
|
||||
[SymbolName("FT_SetLatencyTimer")]
|
||||
public delegate FT.Status SetLatencyTimer(
|
||||
IntPtr handle,
|
||||
byte timerMS);
|
||||
|
||||
[SymbolName("FT_Purge")]
|
||||
public delegate FT.Status Purge(
|
||||
IntPtr handle,
|
||||
FT.PurgeMask mask);
|
||||
|
||||
[SymbolName("FT_SetBreakOn")]
|
||||
public delegate FT.Status SetBreakOn(
|
||||
IntPtr handle);
|
||||
|
||||
[SymbolName("FT_SetBreakOff")]
|
||||
public delegate FT.Status SetBreakOff(
|
||||
IntPtr handle);
|
||||
|
||||
[SymbolName("FT_Read")]
|
||||
public delegate FT.Status Read(
|
||||
IntPtr handle,
|
||||
byte[] buffer,
|
||||
uint countOfBytesToRead,
|
||||
out uint countOfBytesRead);
|
||||
|
||||
[SymbolName("FT_Write")]
|
||||
public delegate FT.Status Write(
|
||||
IntPtr handle,
|
||||
byte[] buffer,
|
||||
uint countOfBytesToWrite,
|
||||
out uint countOfBytesWritten);
|
||||
|
||||
[SymbolName("FT_CreateDeviceInfoList")]
|
||||
public delegate FT.Status CreateDeviceInfoList(
|
||||
out UInt32 numdevs);
|
||||
|
||||
[SymbolName("FT_GetDeviceInfoDetail")]
|
||||
public delegate FT.Status GetDeviceInfoDetail(
|
||||
UInt32 index,
|
||||
out UInt32 flags,
|
||||
out byte chiptype,
|
||||
out UInt32 id,
|
||||
out UInt32 locid,
|
||||
byte[] serialnumber,
|
||||
byte[] description,
|
||||
out IntPtr ftHandle);
|
||||
|
||||
[SymbolName("FT_ReadEE")]
|
||||
public delegate FT.Status ReadEE(
|
||||
IntPtr ftHandle,
|
||||
uint dwWordOffset,
|
||||
ref ushort lpwValue);
|
||||
|
||||
[SymbolName("FT_EE_UASize")]
|
||||
public delegate FT.Status EE_UASize(
|
||||
IntPtr ftHandle,
|
||||
ref uint dwSize);
|
||||
|
||||
[SymbolName("FT_EE_UARead")]
|
||||
public delegate FT.Status EE_UARead(
|
||||
IntPtr ftHandle,
|
||||
byte[] pucData,
|
||||
int dwDataLen,
|
||||
ref uint lpdwDataRead);
|
||||
|
||||
[SymbolName("FT_EE_UAWrite")]
|
||||
public delegate FT.Status EE_UAWrite(
|
||||
IntPtr ftHandle,
|
||||
byte[] pucData,
|
||||
int dwDataLen);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
17
Infrastructure/Kwp/IInterface.cs
Normal file
17
Infrastructure/Kwp/IInterface.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace HC_APTBS.Infrastructure.Kwp
|
||||
{
|
||||
public interface IInterface : IDisposable
|
||||
{
|
||||
byte ReadByte();
|
||||
|
||||
void WriteByteRaw(byte b);
|
||||
|
||||
void SetBreakOn();
|
||||
|
||||
void SetBreakOff();
|
||||
|
||||
void ClearReceiveBuffer();
|
||||
}
|
||||
}
|
||||
70
Infrastructure/Kwp/KLineInterface.cs
Normal file
70
Infrastructure/Kwp/KLineInterface.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.IO.Ports;
|
||||
|
||||
namespace HC_APTBS.Infrastructure.Kwp
|
||||
{
|
||||
class KLineInterface : IInterface
|
||||
{
|
||||
public KLineInterface(string portName, int baudRate)
|
||||
{
|
||||
_port = new SerialPort(portName)
|
||||
{
|
||||
BaudRate = baudRate,
|
||||
DataBits = 8,
|
||||
Parity = Parity.None,
|
||||
StopBits = StopBits.One,
|
||||
Handshake = Handshake.None,
|
||||
RtsEnable = false,
|
||||
DtrEnable = true,
|
||||
ReadTimeout = 1000,
|
||||
WriteTimeout = 500
|
||||
};
|
||||
|
||||
_port.Open();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_port.Close();
|
||||
}
|
||||
|
||||
public byte ReadByte()
|
||||
{
|
||||
try
|
||||
{
|
||||
var b = (byte)_port.ReadByte();
|
||||
return b;
|
||||
}
|
||||
catch (TimeoutException ex)
|
||||
{
|
||||
throw new TimeoutException("Read timed out");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void WriteByteRaw(byte b)
|
||||
{
|
||||
_buf[0] = b;
|
||||
_port.Write(_buf, 0, 1);
|
||||
}
|
||||
|
||||
public void SetBreakOn()
|
||||
{
|
||||
_port.BreakState = true;
|
||||
}
|
||||
|
||||
public void SetBreakOff()
|
||||
{
|
||||
_port.BreakState = false;
|
||||
}
|
||||
|
||||
public void ClearReceiveBuffer()
|
||||
{
|
||||
_port.DiscardInBuffer();
|
||||
}
|
||||
|
||||
private readonly SerialPort _port;
|
||||
|
||||
private readonly byte[] _buf = new byte[1];
|
||||
}
|
||||
}
|
||||
577
Infrastructure/Kwp/KW1281Connection.cs
Normal file
577
Infrastructure/Kwp/KW1281Connection.cs
Normal file
@@ -0,0 +1,577 @@
|
||||
using HC_APTBS.Infrastructure.Kwp.Packets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace HC_APTBS.Infrastructure.Kwp
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages a dialog with a VW controller using the KW1281 protocol.
|
||||
/// </summary>
|
||||
public interface IKW1281Connection
|
||||
{
|
||||
ControllerInfo ReadEcuInfo();
|
||||
|
||||
void EndCommunication();
|
||||
|
||||
List<ControllerIdent> ReadIdent();
|
||||
|
||||
List<byte> ReadEeprom(ushort address, byte count);
|
||||
|
||||
List<byte> ReadRomEeprom(ushort address, byte count);
|
||||
|
||||
void CustomReset();
|
||||
|
||||
void SendPacket(List<byte> packetBytes);
|
||||
|
||||
List<Packet> SendCustom(List<byte> packetCustomBytes);
|
||||
|
||||
/// <summary>
|
||||
/// Keep the dialog alive by sending an ACK and receiving a response.
|
||||
/// </summary>
|
||||
void KeepAlive();
|
||||
|
||||
List<FaultCode> ReadFaultCodes();
|
||||
|
||||
/// <summary>
|
||||
/// Clear all of the controllers fault codes.
|
||||
/// </summary>
|
||||
/// <returns>True if successful.</returns>
|
||||
bool ClearFaultCodes();
|
||||
|
||||
/// <summary>
|
||||
/// Set the controller's software coding and workshop code.
|
||||
/// </summary>
|
||||
/// <param name="controllerAddress"></param>
|
||||
/// <param name="softwareCoding"></param>
|
||||
/// <param name="workshopCode"></param>
|
||||
/// <returns>True if successful.</returns>
|
||||
bool SetSoftwareCoding(int controllerAddress, int softwareCoding, int workshopCode);
|
||||
|
||||
IKwpCommon KwpCommon { get; }
|
||||
}
|
||||
|
||||
public class KW1281Connection : IKW1281Connection
|
||||
{
|
||||
public ControllerInfo ReadEcuInfo()
|
||||
{
|
||||
var packets = ReceivePackets();
|
||||
return new ControllerInfo(packets.Where(b => !b.IsAckNak));
|
||||
}
|
||||
|
||||
public ControllerInfo ReadEcuInfoCustom(Int32 pkt_count)
|
||||
{
|
||||
var packets = new List<Packet>();
|
||||
|
||||
for(var i=0;i<pkt_count; i++)
|
||||
{
|
||||
var packet = ReceivePacket();
|
||||
packets.Add(packet); // TODO: Maybe don't add the packet if it's an Ack
|
||||
if (packet.Bytes.Count < 0x10) {
|
||||
break;
|
||||
}
|
||||
if (packet is AckPacket || packet is NakPacket)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (i == pkt_count - 1) break;
|
||||
SendAckPacket();
|
||||
}
|
||||
return new ControllerInfo(packets.Where(b => !b.IsAckNak));
|
||||
}
|
||||
|
||||
public List<ControllerIdent> ReadIdent()
|
||||
{
|
||||
var idents = new List<ControllerIdent>();
|
||||
bool moreAvailable;
|
||||
do
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("Sending ReadIdent packet");
|
||||
|
||||
SendPacket(new List<byte> { (byte)PacketCommand.ReadIdent });
|
||||
|
||||
var packets = ReceivePackets();
|
||||
var ident = new ControllerIdent(packets.Where(b => !b.IsAckNak));
|
||||
idents.Add(ident);
|
||||
|
||||
moreAvailable = packets
|
||||
.OfType<AsciiDataPacket>()
|
||||
.Any(b => b.MoreDataAvailable);
|
||||
} while (moreAvailable);
|
||||
|
||||
return idents;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads a range of bytes from the EEPROM.
|
||||
/// </summary>
|
||||
/// <param name="address"></param>
|
||||
/// <param name="count"></param>
|
||||
/// <returns>The bytes or null if the bytes could not be read</returns>
|
||||
public List<byte> ReadEeprom(ushort address, byte count)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Sending ReadEeprom packet (Address: ${address:X4}, Count: ${count:X2})");
|
||||
SendPacket(new List<byte>
|
||||
{
|
||||
(byte)PacketCommand.ReadEeprom,
|
||||
count,
|
||||
(byte)(address >> 8),
|
||||
(byte)(address & 0xFF)
|
||||
});
|
||||
var packets = ReceivePackets();
|
||||
|
||||
if (packets.Count == 1 && packets[0] is NakPacket)
|
||||
{
|
||||
// Permissions issue
|
||||
return null;
|
||||
}
|
||||
|
||||
packets = packets.Where(b => !b.IsAckNak).ToList();
|
||||
if (packets.Count != 1)
|
||||
{
|
||||
throw new InvalidOperationException($"ReadEeprom returned {packets.Count} blocks instead of 1");
|
||||
}
|
||||
return packets[0].Body.ToList();
|
||||
}
|
||||
|
||||
public List<byte> ReadRomEeprom(ushort address, byte count)
|
||||
{
|
||||
//System.Diagnostics.Debug.WriteLine($"Sending ReadRomEeprom packet (Address: ${address:X4}, Count: ${count:X2})");
|
||||
SendPacket(new List<byte>
|
||||
{
|
||||
(byte)PacketCommand.ReadRomEeprom,
|
||||
count,
|
||||
(byte)(address >> 8),
|
||||
(byte)(address & 0xFF)
|
||||
});
|
||||
var packets = ReceivePackets();
|
||||
|
||||
if (packets.Count == 1 && packets[0] is NakPacket)
|
||||
{
|
||||
return new List<byte>();
|
||||
}
|
||||
|
||||
packets = packets.Where(b => !b.IsAckNak).ToList();
|
||||
if (packets.Count != 1)
|
||||
{
|
||||
throw new InvalidOperationException($"ReadRomEeprom returned {packets.Count} blocks instead of 1");
|
||||
}
|
||||
return packets[0].Body.ToList();
|
||||
}
|
||||
|
||||
|
||||
public Dictionary<int, Packet> CustomReadSoftwareVersion()
|
||||
{
|
||||
var versionPackets = new Dictionary<int, Packet>();
|
||||
|
||||
System.Diagnostics.Debug.WriteLine("Sending Custom \"Read Software Version\" packets");
|
||||
|
||||
// The cluster can return 4 variations of software version, specified by the 2nd byte
|
||||
// of the packet:
|
||||
// 0x00 - Cluster software version
|
||||
// 0x01 - Unknown
|
||||
// 0x02 - Unknown
|
||||
// 0x03 - Unknown
|
||||
for (byte variation = 0x00; variation < 0x04; variation++)
|
||||
{
|
||||
var packets = SendCustom(new List<byte> { 0x84, variation });
|
||||
foreach (var packet in packets.Where(b => !b.IsAckNak))
|
||||
{
|
||||
if (variation == 0x00 || variation == 0x03)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"{variation:X2}: {DumpMixedContent(packet)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"{variation:X2}: {DumpBinaryContent(packet)}");
|
||||
}
|
||||
versionPackets[variation] = packet;
|
||||
}
|
||||
}
|
||||
|
||||
return versionPackets;
|
||||
}
|
||||
|
||||
private static string DumpMixedContent(Packet packet)
|
||||
{
|
||||
if (packet.IsNak)
|
||||
{
|
||||
return "NAK";
|
||||
}
|
||||
|
||||
return DumpMixedContent(packet.Body);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Todo: Move to utility class
|
||||
/// </summary>
|
||||
public static string DumpMixedContent(IEnumerable<byte> content)
|
||||
{
|
||||
char mode = '?';
|
||||
var sb = new StringBuilder();
|
||||
foreach (var b in content)
|
||||
{
|
||||
if (b >= 32 && b <= 126)
|
||||
{
|
||||
mode = 'A';
|
||||
|
||||
sb.Append((char)b);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mode == 'A')
|
||||
{
|
||||
sb.Append(' ');
|
||||
}
|
||||
mode = 'X';
|
||||
|
||||
sb.Append($"${b:X2} ");
|
||||
}
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string DumpBinaryContent(Packet packet)
|
||||
{
|
||||
if (packet.IsNak)
|
||||
{
|
||||
return "NAK";
|
||||
}
|
||||
|
||||
return DumpBytes(packet.Body);
|
||||
}
|
||||
|
||||
private static string DumpBytes(IEnumerable<byte> bytes)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (var b in bytes)
|
||||
{
|
||||
sb.Append($"${b:X2} ");
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public void CustomReset()
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("Sending Custom Reset packet");
|
||||
SendCustom(new List<byte> { 0x82 });
|
||||
}
|
||||
|
||||
public List<Packet> SendCustom(List<byte> packetCustomBytes)
|
||||
{
|
||||
SendPacket(packetCustomBytes);
|
||||
return ReceivePackets();
|
||||
}
|
||||
|
||||
public void EndCommunication()
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("Sending EndCommunication packet");
|
||||
SendPacket(new List<byte> { (byte)PacketCommand.End });
|
||||
}
|
||||
|
||||
public void SendPacket(List<byte> packetBytes)
|
||||
{
|
||||
var packetLength = (byte)(packetBytes.Count + 2);
|
||||
|
||||
packetBytes.Insert(0, _packetCounter.Value);
|
||||
_packetCounter++;
|
||||
|
||||
packetBytes.Insert(0, packetLength);
|
||||
|
||||
foreach (var b in packetBytes)
|
||||
{
|
||||
WriteByteAndReadAck(b);
|
||||
Thread.Sleep(5);
|
||||
}
|
||||
|
||||
KwpCommon.WriteByte(0x03); // Packet end, does not get ACK'd
|
||||
}
|
||||
|
||||
private List<Packet> ReceivePackets()
|
||||
{
|
||||
var packets = new List<Packet>();
|
||||
|
||||
while (true)
|
||||
{
|
||||
var packet = ReceivePacket();
|
||||
packets.Add(packet); // TODO: Maybe don't add the packet if it's an Ack
|
||||
if (packet is AckPacket || packet is NakPacket)
|
||||
{
|
||||
break;
|
||||
}
|
||||
SendAckPacket();
|
||||
}
|
||||
|
||||
return packets;
|
||||
}
|
||||
|
||||
private void WriteByteAndReadAck(byte b)
|
||||
{
|
||||
KwpCommon.WriteByte(b);
|
||||
KwpCommon.ReadComplement(b);
|
||||
}
|
||||
|
||||
private Packet ReceivePacket()
|
||||
{
|
||||
var packetBytes = new List<byte>();
|
||||
|
||||
var packetLength = KwpCommon.ReadAndAckByte();
|
||||
packetBytes.Add(packetLength);
|
||||
|
||||
var packetCounter = ReadPacketCounter();
|
||||
packetBytes.Add(packetCounter);
|
||||
|
||||
var packetCommand = KwpCommon.ReadAndAckByte();
|
||||
packetBytes.Add(packetCommand);
|
||||
|
||||
for (int i = 0; i < packetLength - 3; i++)
|
||||
{
|
||||
var b = KwpCommon.ReadAndAckByte();
|
||||
packetBytes.Add(b);
|
||||
}
|
||||
|
||||
var packetEnd = KwpCommon.ReadByte();
|
||||
packetBytes.Add(packetEnd);
|
||||
if (packetEnd != 0x03)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Received packet end ${packetEnd:X2} but expected $03");
|
||||
}
|
||||
|
||||
|
||||
switch (packetCommand)
|
||||
{
|
||||
case (byte)PacketCommand.ACK:
|
||||
return new AckPacket(packetBytes);
|
||||
|
||||
case (byte)PacketCommand.AsciiData:
|
||||
if (packetBytes[3] == 0x00) return new CodingWscPacket(packetBytes);
|
||||
return new AsciiDataPacket(packetBytes);
|
||||
|
||||
case (byte)PacketCommand.ReadEepromResponse:
|
||||
return new ReadEepromResponsePacket(packetBytes);
|
||||
|
||||
case (byte)PacketCommand.ReadRomEepromResponse:
|
||||
return new ReadRomEepromResponse(packetBytes);
|
||||
|
||||
case (byte)PacketCommand.Custom:
|
||||
return new CustomPacket(packetBytes);
|
||||
break;
|
||||
|
||||
case (byte)PacketCommand.NAK:
|
||||
return new NakPacket(packetBytes);
|
||||
|
||||
case (byte)PacketCommand.FaultCodesResponse:
|
||||
return new FaultCodesPacket(packetBytes);
|
||||
|
||||
case (byte)PacketCommand.WriteEepromResponse:
|
||||
return new WriteEepromResponsePacket(packetBytes);
|
||||
|
||||
default:
|
||||
return new UnknownPacket(packetBytes);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void SendAckPacket()
|
||||
{
|
||||
var packetBytes = new List<byte> { (byte)PacketCommand.ACK };
|
||||
SendPacket(packetBytes);
|
||||
}
|
||||
|
||||
private byte ReadPacketCounter()
|
||||
{
|
||||
var packetCounter = KwpCommon.ReadAndAckByte();
|
||||
if (!_packetCounter.HasValue)
|
||||
{
|
||||
// First packet
|
||||
_packetCounter = packetCounter;
|
||||
}
|
||||
else if (packetCounter != _packetCounter)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Received packet counter ${packetCounter:X2} but expected ${_packetCounter:X2}");
|
||||
}
|
||||
_packetCounter++;
|
||||
return packetCounter;
|
||||
}
|
||||
|
||||
public void KeepAlive()
|
||||
{
|
||||
SendAckPacket();
|
||||
var packet = ReceivePacket();
|
||||
if (!(packet is AckPacket))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Received 0x{packet.Title:X2} packet but expected ACK");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public List<FaultCode> ReadFaultCodes()
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Sending ReadFaultCodes packet");
|
||||
SendPacket(new List<byte>
|
||||
{
|
||||
(byte)PacketCommand.FaultCodesRead
|
||||
});
|
||||
|
||||
var packets = ReceivePackets();
|
||||
packets = packets.Where(b => !b.IsAckNak).ToList();
|
||||
|
||||
var faultCodes = new List<FaultCode>();
|
||||
var faultCodesData = new List<byte>();
|
||||
foreach (var packet in packets)
|
||||
{
|
||||
if (!(packet is FaultCodesPacket))
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Expected FaultCodesPacket but got {packet.GetType()}");
|
||||
return null;
|
||||
}
|
||||
|
||||
faultCodesData.AddRange(packet.Body);
|
||||
}
|
||||
|
||||
IEnumerable<byte> data = faultCodesData;
|
||||
while (true)
|
||||
{
|
||||
var code = data.Take(3).ToArray();
|
||||
if (code.Length == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var dtc = code[0];
|
||||
var status = code[1];
|
||||
|
||||
var faultCode = new FaultCode(dtc, status);
|
||||
if (faultCode.Dtc != FaultCode.None.Dtc)
|
||||
{
|
||||
faultCodes.Add(faultCode);
|
||||
}
|
||||
|
||||
data = data.Skip(8);
|
||||
}
|
||||
|
||||
return faultCodes;
|
||||
}
|
||||
|
||||
public bool ClearFaultCodes()
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Sending ClearFaultCodes packet");
|
||||
SendPacket(new List<byte>
|
||||
{
|
||||
(byte)PacketCommand.FaultCodesDelete
|
||||
});
|
||||
|
||||
var packets = ReceivePackets();
|
||||
if (packets.Count == 1)
|
||||
{
|
||||
var packet = packets[0];
|
||||
if (packet is NakPacket)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (packet is AckPacket)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"ClearFaultCodes returned {packet.GetType()} packet instead of ACK/NAK");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"ClearFaultCodes returned {packets.Count} packets instead of 1");
|
||||
}
|
||||
}
|
||||
|
||||
public bool SetSoftwareCoding(int controllerAddress, int softwareCoding, int workshopCode)
|
||||
{
|
||||
// Workshop codes > 65535 overflow into the low bit of the software coding
|
||||
var bytes = new List<byte>
|
||||
{
|
||||
(byte)PacketCommand.SoftwareCoding,
|
||||
(byte)((softwareCoding * 2) / 256),
|
||||
(byte)((softwareCoding * 2) % 256),
|
||||
(byte)((workshopCode & 65535) / 256),
|
||||
(byte)(workshopCode % 256)
|
||||
};
|
||||
|
||||
if (workshopCode > 65535)
|
||||
{
|
||||
bytes[2]++;
|
||||
}
|
||||
|
||||
System.Diagnostics.Debug.WriteLine($"Sending SoftwareCoding packet");
|
||||
SendPacket(bytes);
|
||||
|
||||
var packets = ReceivePackets();
|
||||
if (packets.Count == 1 && packets[0] is NakPacket)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var controllerInfo = new ControllerInfo(packets.Where(b => !b.IsAckNak));
|
||||
return
|
||||
controllerInfo.SoftwareCoding == softwareCoding &&
|
||||
controllerInfo.WorkshopCode == workshopCode;
|
||||
}
|
||||
|
||||
public IKwpCommon KwpCommon { get; }
|
||||
|
||||
private byte? _packetCounter = null;
|
||||
|
||||
public KW1281Connection(IKwpCommon kwpCommon)
|
||||
{
|
||||
KwpCommon = kwpCommon;
|
||||
}
|
||||
}
|
||||
|
||||
public class KW1281KeepAlive : IDisposable
|
||||
{
|
||||
private readonly IKW1281Connection _kw1281Connection;
|
||||
private volatile bool _cancel = false;
|
||||
private Task _keepAliveTask = null;
|
||||
|
||||
public KW1281KeepAlive(IKW1281Connection kw1281Connection)
|
||||
{
|
||||
_kw1281Connection = kw1281Connection;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Pause();
|
||||
}
|
||||
|
||||
private void Pause()
|
||||
{
|
||||
_cancel = true;
|
||||
if (_keepAliveTask != null)
|
||||
{
|
||||
_keepAliveTask.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
private void Resume()
|
||||
{
|
||||
_keepAliveTask = Task.Run(KeepAlive);
|
||||
}
|
||||
|
||||
private void KeepAlive()
|
||||
{
|
||||
_cancel = false;
|
||||
while (!_cancel)
|
||||
{
|
||||
_kw1281Connection.KeepAlive();
|
||||
Console.Write(".");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
162
Infrastructure/Kwp/KwpCommon.cs
Normal file
162
Infrastructure/Kwp/KwpCommon.cs
Normal file
@@ -0,0 +1,162 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace HC_APTBS.Infrastructure.Kwp
|
||||
{
|
||||
public interface IKwpCommon
|
||||
{
|
||||
IInterface Interface { get; }
|
||||
|
||||
int WakeUp(byte controllerAddress, bool evenParity = false);
|
||||
|
||||
byte ReadByte();
|
||||
|
||||
void WriteByte(byte b);
|
||||
|
||||
byte ReadAndAckByte();
|
||||
|
||||
void ReadComplement(byte b);
|
||||
}
|
||||
|
||||
public class KwpCommon : IKwpCommon
|
||||
{
|
||||
public IInterface Interface => _interface;
|
||||
|
||||
public int WakeUp(byte controllerAddress, bool evenParity = false)
|
||||
{
|
||||
// Disable garbage collection in this time-critical method
|
||||
bool noGc = GC.TryStartNoGCRegion(1024 * 1024 * 128); //antes 16
|
||||
|
||||
BitBang5Baud(controllerAddress, evenParity);
|
||||
|
||||
if (noGc)
|
||||
{
|
||||
GC.EndNoGCRegion();
|
||||
}
|
||||
|
||||
// Throw away anything that might be in the receive buffer
|
||||
_interface.ClearReceiveBuffer();
|
||||
|
||||
System.Diagnostics.Debug.WriteLine("Reading sync byte");
|
||||
var syncByte = _interface.ReadByte();
|
||||
if (syncByte != 0x55)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Unexpected sync byte: Expected $55, Actual ${syncByte:X2}");
|
||||
}
|
||||
|
||||
var keywordLsb = _interface.ReadByte();
|
||||
System.Diagnostics.Debug.WriteLine($"Keyword LSB: 0x{keywordLsb:X2}");
|
||||
|
||||
var keywordMsb = ReadAndAckByte();
|
||||
|
||||
System.Diagnostics.Debug.WriteLine($"Keyword MSB: 0x{keywordMsb:X2}");
|
||||
|
||||
var protocolVersion = ((keywordMsb & 0x7F) << 7) + (keywordLsb & 0x7F);
|
||||
|
||||
return protocolVersion;
|
||||
}
|
||||
|
||||
public byte ReadByte()
|
||||
{
|
||||
return _interface.ReadByte();
|
||||
}
|
||||
|
||||
public void WriteByte(byte b)
|
||||
{
|
||||
WriteByteAndDiscardEcho(b);
|
||||
}
|
||||
|
||||
public byte ReadAndAckByte()
|
||||
{
|
||||
var b = _interface.ReadByte();
|
||||
WriteComplement(b);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
public void ReadComplement(byte b)
|
||||
{
|
||||
var expectedComplement = (byte)~b;
|
||||
var actualComplement = _interface.ReadByte();
|
||||
if (actualComplement != expectedComplement)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Received complement ${actualComplement:X2} but expected ${expectedComplement:X2}");
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteComplement(byte b)
|
||||
{
|
||||
var complement = (byte)~b;
|
||||
WriteByteAndDiscardEcho(complement);
|
||||
}
|
||||
|
||||
private void BitBang5Baud(byte b, bool evenParity)
|
||||
{
|
||||
const int bitsPerSec = 5;
|
||||
long ticksPerBit = Stopwatch.Frequency / bitsPerSec;
|
||||
|
||||
long maxTick;
|
||||
|
||||
// Delay the appropriate amount and then set/clear the TxD line
|
||||
void BitBang(bool bit)
|
||||
{
|
||||
while (Stopwatch.GetTimestamp() < maxTick)
|
||||
;
|
||||
if (bit)
|
||||
{
|
||||
_interface.SetBreakOff();
|
||||
}
|
||||
else
|
||||
{
|
||||
_interface.SetBreakOn();
|
||||
}
|
||||
|
||||
maxTick += ticksPerBit;
|
||||
}
|
||||
|
||||
bool parity = !evenParity;
|
||||
|
||||
maxTick = Stopwatch.GetTimestamp();
|
||||
BitBang(false); // Start bit
|
||||
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
bool bit = (b & 1) == 1;
|
||||
parity ^= bit;
|
||||
b >>= 1;
|
||||
|
||||
BitBang(bit);
|
||||
}
|
||||
|
||||
BitBang(parity);
|
||||
|
||||
BitBang(true); // Stop bit
|
||||
|
||||
// Wait for end of stop bit
|
||||
while (Stopwatch.GetTimestamp() < maxTick)
|
||||
;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a byte to the interface and read/discard its echo.
|
||||
/// </summary>
|
||||
private void WriteByteAndDiscardEcho(byte b)
|
||||
{
|
||||
_interface.WriteByteRaw(b);
|
||||
var echo = _interface.ReadByte();
|
||||
if (echo != b)
|
||||
{
|
||||
throw new InvalidOperationException($"Wrote 0x{b:X2} to port but echo was 0x{echo:X2}");
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IInterface _interface;
|
||||
|
||||
public KwpCommon(IInterface @interface)
|
||||
{
|
||||
_interface = @interface;
|
||||
}
|
||||
}
|
||||
}
|
||||
28
Infrastructure/Kwp/PacketCommand.cs
Normal file
28
Infrastructure/Kwp/PacketCommand.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
namespace HC_APTBS.Infrastructure.Kwp
|
||||
{
|
||||
public enum PacketCommand : byte
|
||||
{
|
||||
ReadIdent = 0x00,
|
||||
ReadRomEeprom = 0x03,
|
||||
ActuatorTest = 0x04,
|
||||
FaultCodesDelete = 0x05,
|
||||
End = 0x06,
|
||||
FaultCodesRead = 0x07,
|
||||
ACK = 0x09,
|
||||
NAK = 0x0A,
|
||||
SoftwareCoding = 0x10,
|
||||
ReadEeprom = 0x19,
|
||||
WriteEeprom = 0x1A,
|
||||
Custom = 0x1B,
|
||||
GroupReading = 0x29,
|
||||
Login = 0x2B,
|
||||
GroupReadingResponse = 0xE7,
|
||||
ReadEepromResponse = 0xEF,
|
||||
ActuatorTestResponse = 0xF5,
|
||||
AsciiData = 0xF6,
|
||||
WriteEepromResponse = 0xF9,
|
||||
FaultCodesResponse = 0xFC,
|
||||
ReadRomEepromResponse = 0xFD,
|
||||
}
|
||||
|
||||
}
|
||||
18
Infrastructure/Kwp/Packets/AckPacket.cs
Normal file
18
Infrastructure/Kwp/Packets/AckPacket.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace HC_APTBS.Infrastructure.Kwp.Packets
|
||||
{
|
||||
public class AckPacket : Packet
|
||||
{
|
||||
public AckPacket(List<byte> bytes) : base(bytes)
|
||||
{
|
||||
Dump();
|
||||
}
|
||||
|
||||
private void Dump()
|
||||
{
|
||||
//Logger.WriteLine("Received ACK packet");
|
||||
}
|
||||
}
|
||||
}
|
||||
38
Infrastructure/Kwp/Packets/AsciiDataPacket.cs
Normal file
38
Infrastructure/Kwp/Packets/AsciiDataPacket.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace HC_APTBS.Infrastructure.Kwp.Packets
|
||||
{
|
||||
public class AsciiDataPacket : Packet
|
||||
{
|
||||
public AsciiDataPacket(List<byte> bytes) : base(bytes)
|
||||
{
|
||||
// Dump();
|
||||
}
|
||||
|
||||
public bool MoreDataAvailable => Bytes[3] > 0x7F;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (var b in Body)
|
||||
{
|
||||
sb.Append((char)(b & 0x7F));
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private void Dump()
|
||||
{
|
||||
System.Diagnostics.Debug.Write($"Received Ascii data packet: \"{ToString()}\"");
|
||||
|
||||
if (MoreDataAvailable)
|
||||
{
|
||||
System.Diagnostics.Debug.Write(" (More data available via ReadIdent)");
|
||||
}
|
||||
|
||||
System.Diagnostics.Debug.WriteLine("");
|
||||
}
|
||||
}
|
||||
}
|
||||
31
Infrastructure/Kwp/Packets/CodingWscPacket.cs
Normal file
31
Infrastructure/Kwp/Packets/CodingWscPacket.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace HC_APTBS.Infrastructure.Kwp.Packets
|
||||
{
|
||||
public class CodingWscPacket : Packet
|
||||
{
|
||||
public CodingWscPacket(List<byte> bytes) : base(bytes)
|
||||
{
|
||||
var data = bytes.Skip(4).ToList();
|
||||
|
||||
SoftwareCoding = (data[0] * 256 + data[1]) / 2;
|
||||
WorkshopCode = data[2] * 256 + data[3];
|
||||
|
||||
// Workshop codes > 65535 overflow into the low bit of the software coding
|
||||
if ((data[1] & 1) == 1)
|
||||
{
|
||||
WorkshopCode += 65536;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Software Coding {SoftwareCoding:d5}, Workshop Code: {WorkshopCode:d5}";
|
||||
}
|
||||
|
||||
public int SoftwareCoding { get; }
|
||||
|
||||
public int WorkshopCode { get; }
|
||||
}
|
||||
}
|
||||
24
Infrastructure/Kwp/Packets/CustomPacket.cs
Normal file
24
Infrastructure/Kwp/Packets/CustomPacket.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace HC_APTBS.Infrastructure.Kwp.Packets
|
||||
{
|
||||
public class CustomPacket : Packet
|
||||
{
|
||||
public CustomPacket(List<byte> bytes) : base(bytes)
|
||||
{
|
||||
// Dump();
|
||||
}
|
||||
|
||||
private void Dump()
|
||||
{
|
||||
System.Diagnostics.Debug.Write("Received Custom packet:");
|
||||
for (var i = 3; i < Bytes.Count - 1; i++)
|
||||
{
|
||||
System.Diagnostics.Debug.Write($" {Bytes[i]:X2}");
|
||||
}
|
||||
|
||||
System.Diagnostics.Debug.WriteLine("");
|
||||
}
|
||||
}
|
||||
}
|
||||
59
Infrastructure/Kwp/Packets/FaultCodesPacket.cs
Normal file
59
Infrastructure/Kwp/Packets/FaultCodesPacket.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace HC_APTBS.Infrastructure.Kwp.Packets
|
||||
{
|
||||
public class FaultCodesPacket : Packet
|
||||
{
|
||||
public FaultCodesPacket(List<byte> bytes) : base(bytes)
|
||||
{
|
||||
|
||||
Data = bytes;
|
||||
|
||||
}
|
||||
public List<byte> Data { get; }
|
||||
}
|
||||
|
||||
public struct FaultCode
|
||||
{
|
||||
public FaultCode(int dtc, int status)
|
||||
{
|
||||
Dtc = dtc;
|
||||
Status = status;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var status1 = Status & 0x7F;
|
||||
var status2 = (Status >> 7) * 10;
|
||||
|
||||
Dictionary<int, string> dtc_text = new Dictionary<int, string>
|
||||
{
|
||||
{0x50, "Fuel quantity solenoid valve Output stage error"},
|
||||
{0x51, "Fuel quantity solenoid valve."},
|
||||
{0x52, "Angle sensor/ IWZ system."},
|
||||
{0x53, "Angle sensor/ IWZ system"},
|
||||
{0x54, "Control unit temperature sensor, temperature to high"},
|
||||
{0x55, "Control unit temperature sensor"},
|
||||
{0x56, "Battery voltage out of range"},
|
||||
{0x57, "Timing device control.Permanent control deviation"},
|
||||
{0x58, "Fuel quantity / timing solenoid valve"},
|
||||
{0x59, "BIP Fault(Begin of Injection Point)"},
|
||||
{0x5A, "Engine speed signal"},
|
||||
{0x5B, "Engine speed signal"},
|
||||
{0x5C, "CAN -Bus(sporadic)"},
|
||||
{0x5D, "CAN bus error"},
|
||||
{0x5E, "Self - test error"},
|
||||
};
|
||||
|
||||
if (dtc_text.ContainsKey(Dtc)) return $"{Dtc:X2} {dtc_text[Dtc]} ({status1:d2})";
|
||||
return $"{Dtc:X2} Unknown Error Code ({status1:d2})";
|
||||
}
|
||||
|
||||
public int Dtc { get; }
|
||||
|
||||
public int Status { get; }
|
||||
|
||||
public static readonly FaultCode None = new FaultCode(0x00, 0xFF);
|
||||
}
|
||||
}
|
||||
18
Infrastructure/Kwp/Packets/NakPacket.cs
Normal file
18
Infrastructure/Kwp/Packets/NakPacket.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace HC_APTBS.Infrastructure.Kwp.Packets
|
||||
{
|
||||
class NakPacket : Packet
|
||||
{
|
||||
public NakPacket(List<byte> bytes) : base(bytes)
|
||||
{
|
||||
Dump();
|
||||
}
|
||||
|
||||
private void Dump()
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("Received NAK packet");
|
||||
}
|
||||
}
|
||||
}
|
||||
31
Infrastructure/Kwp/Packets/Packet.cs
Normal file
31
Infrastructure/Kwp/Packets/Packet.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace HC_APTBS.Infrastructure.Kwp.Packets
|
||||
{
|
||||
/// <summary>
|
||||
/// KWP1281 packet
|
||||
/// </summary>
|
||||
public class Packet
|
||||
{
|
||||
public Packet(List<byte> bytes)
|
||||
{
|
||||
Bytes = bytes;
|
||||
}
|
||||
|
||||
public List<byte> Bytes { get; }
|
||||
|
||||
public byte Title => Bytes[2];
|
||||
|
||||
/// <summary>
|
||||
/// Returns the body of the packet, excluding the length, counter, command and end bytes.
|
||||
/// </summary>
|
||||
public List<byte> Body => Bytes.Skip(3).Take(Bytes.Count - 4).ToList();
|
||||
|
||||
public bool IsAck => Title == (byte)PacketCommand.ACK;
|
||||
|
||||
public bool IsNak => Title == (byte)PacketCommand.NAK;
|
||||
|
||||
public bool IsAckNak => IsAck || IsNak;
|
||||
}
|
||||
}
|
||||
24
Infrastructure/Kwp/Packets/ReadEepromResponsePacket.cs
Normal file
24
Infrastructure/Kwp/Packets/ReadEepromResponsePacket.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace HC_APTBS.Infrastructure.Kwp.Packets
|
||||
{
|
||||
public class ReadEepromResponsePacket : Packet
|
||||
{
|
||||
public ReadEepromResponsePacket(List<byte> bytes) : base(bytes)
|
||||
{
|
||||
//Dump();
|
||||
}
|
||||
|
||||
private void Dump()
|
||||
{
|
||||
System.Diagnostics.Debug.Write("Received \"Read EEPROM Response\" packet:");
|
||||
foreach (var b in Body)
|
||||
{
|
||||
System.Diagnostics.Debug.Write($" {b:X2}");
|
||||
}
|
||||
|
||||
System.Diagnostics.Debug.WriteLine("");
|
||||
}
|
||||
}
|
||||
}
|
||||
23
Infrastructure/Kwp/Packets/ReadRomEepromResponse.cs
Normal file
23
Infrastructure/Kwp/Packets/ReadRomEepromResponse.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace HC_APTBS.Infrastructure.Kwp.Packets
|
||||
{
|
||||
public class ReadRomEepromResponse : Packet
|
||||
{
|
||||
public ReadRomEepromResponse(List<byte> bytes) : base(bytes)
|
||||
{
|
||||
//Dump();
|
||||
}
|
||||
|
||||
private void Dump()
|
||||
{
|
||||
System.Diagnostics.Debug.Write("Received \"Read ROM/EEPROM Response\" packet:");
|
||||
foreach (var b in Body)
|
||||
{
|
||||
System.Diagnostics.Debug.Write($" {b:X2}");
|
||||
}
|
||||
|
||||
System.Diagnostics.Debug.WriteLine("");
|
||||
}
|
||||
}
|
||||
}
|
||||
23
Infrastructure/Kwp/Packets/UnknownPacket.cs
Normal file
23
Infrastructure/Kwp/Packets/UnknownPacket.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace HC_APTBS.Infrastructure.Kwp.Packets
|
||||
{
|
||||
public class UnknownPacket : Packet
|
||||
{
|
||||
public UnknownPacket(List<byte> bytes) : base(bytes)
|
||||
{
|
||||
Dump();
|
||||
}
|
||||
|
||||
private void Dump()
|
||||
{
|
||||
/*Logger.Write("Received unknown packet");
|
||||
foreach (var b in Bytes)
|
||||
{
|
||||
Logger.Write($" 0x{b:X2}");
|
||||
}
|
||||
Logger.WriteLine();*/
|
||||
}
|
||||
}
|
||||
}
|
||||
24
Infrastructure/Kwp/Packets/WriteEepromResponsePacket.cs
Normal file
24
Infrastructure/Kwp/Packets/WriteEepromResponsePacket.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace HC_APTBS.Infrastructure.Kwp.Packets
|
||||
{
|
||||
public class WriteEepromResponsePacket : Packet
|
||||
{
|
||||
public WriteEepromResponsePacket(List<byte> bytes) : base(bytes)
|
||||
{
|
||||
Dump();
|
||||
}
|
||||
|
||||
private void Dump()
|
||||
{
|
||||
System.Diagnostics.Debug.Write("Received \"Write EEPROM Response\" packet:");
|
||||
foreach (var b in Body)
|
||||
{
|
||||
System.Diagnostics.Debug.Write($" {b:X2}");
|
||||
}
|
||||
|
||||
System.Diagnostics.Debug.WriteLine("");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user