initial commit

This commit is contained in:
2026-04-11 12:45:18 +02:00
commit 6e1b929e2f
1246 changed files with 177580 additions and 0 deletions

View 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;
}
}
}

View 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;
}
}
}

View 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);
}
}

View 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();
}
}

View 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];
}
}

View 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(".");
}
}
}
}

View 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;
}
}
}

View 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,
}
}

View 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");
}
}
}

View 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("");
}
}
}

View 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; }
}
}

View 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("");
}
}
}

View 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);
}
}

View 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");
}
}
}

View 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;
}
}

View 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("");
}
}
}

View 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("");
}
}
}

View 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();*/
}
}
}

View 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("");
}
}
}