using System;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading;
using LinkSupport;
using ThreadSafe;

namespace OBD
{
	public class OBDHelper
	{
		public static byte GetCheckSum(byte[] Arr, bool IgnoreLastByte)
		{
			int res = 0;
			if (Arr != null && Arr.Length > 0)
			{
				int cnt = Arr.Length;
				if (IgnoreLastByte)
					cnt--;
				res = 256;
				if (cnt > 0)
				{
					for (int i = 0; i < cnt; i++)
					{
						res = res - Arr[i];
						if (res < 0)
							res = 256 + res;
					}
				}
			}
			return (byte)res;
		}
		public static byte[] GetSendBytesFor20H(byte offset, byte cnt)
		{
			byte[] res = new byte[5];
			res[0] = 0x20;
			res[1] = 0x05;
			res[2] = offset;
			res[3] = cnt;
			res[4] = GetCheckSum(res, true);
			return res;
		}
		public static string GetHexString(byte[] arr)
		{
			string res = "";
			if (arr != null && arr.Length > 0)
			{
				for (int i = 0; i < arr.Length; i++)
				{
					res += i == 0 ? arr[i].ToString("X2") : " " + arr[i].ToString("X2");
				}
			}
			return res;
		}
		public static byte[] GetBytesFromHexString(string s)
		{
			byte[] res = new byte[0];
			if (!string.IsNullOrEmpty(s))
			{
				string[] bytesStr = s.Split(' ');
				if (bytesStr.Length > 0)
				{
					for (int i = 0; i < bytesStr.Length; i++)
					{
						if (!string.IsNullOrEmpty(bytesStr[i]))
						{
							if (res == null)
								res = new byte[0];
							Array.Resize(ref res, res.Length + 1);
							res[res.Length - 1] = byte.Parse(bytesStr[i], NumberStyles.AllowHexSpecifier);
						}
					}
				}
			}
			return res;
		}
		public static byte[] DecLenDataArr(byte[] arr)
		{
			byte[] res = new byte[0];
			if (arr != null)
			{
				if (arr.Length > 0)
				{
					byte[] tmp = new byte[arr.Length - 1];
					Array.Copy(arr, 1, tmp, 0, tmp.Length);
					res = tmp;
				}
			}
			return res;
		}

		public static byte GetLenOBDSensorAnswer(ValuesOffset offs)
		{
			byte res = 1;
			switch(offs)
			{
				case ValuesOffset.RPM:
				case ValuesOffset.Inj:
				case ValuesOffset.RPM2:
					res = 2;
					break;
				case ValuesOffset.ECUID:
					res = 10;
					break;
				case ValuesOffset.Errors1:
				case ValuesOffset.Errors2:
					res = 0x10;
					break;
			}
			return res;
		}

		#region Arrays Values
		public static int[] Mbar =  { 
	-29, -22, -15, -8, -1, 5, 13, 20, 27, 34, 41, 49, 56, 63, 70, 
	77, 84, 92, 99, 106, 113, 120, 127, 135, 142, 149, 156, 163, 170, 178, 185, 
	192, 199, 206, 214, 221, 228, 235, 242, 249, 257, 264, 271, 278, 285, 292, 
	300, 307, 314, 321, 328, 336, 343, 350, 357, 364, 371, 379, 386, 393, 400, 
	407, 414, 422, 429, 436, 443, 450, 457, 465, 472, 479, 486, 493, 501, 508, 
	515, 522, 529, 536, 544, 551, 558, 565, 572, 579, 587, 594, 601, 608, 615, 
	622, 630, 637, 644, 651, 658, 666, 673, 680, 687, 694, 701, 709, 716, 723, 
	730, 737, 744, 752, 759, 766, 773, 780, 787, 795, 802, 809, 816, 823, 831, 
	838, 845, 852, 859, 866, 874, 881, 888, 895, 902, 909, 917, 924, 931, 938, 
	945, 953, 960, 967, 974, 981, 988, 996, 1003, 1010, 1017, 1024, 1031, 1039, 
	1046, 1053, 1060, 1067, 1074, 1082, 1089, 1096, 1103, 1110, 1118, 1125, 1132, 
	1139, 1146, 1153, 1161, 1168, 1175, 1182, 1189, 1196, 1204, 1211, 1218, 1225, 
	1232, 1239, 1247, 1254, 1261, 1268, 1275, 1283, 1290, 1297, 1304, 1311, 1318, 
	1326, 1333, 1340, 1347, 1354, 1361, 1369, 1376, 1383, 1390, 1397, 1405, 1412, 
	1419, 1426, 1433, 1440, 1448, 1455, 1462, 1469, 1476, 1483, 1491, 1498, 1505, 
	1512, 1519, 1526, 1534, 1541, 1548, 1555, 1562, 1570, 1577, 1584, 1591, 1598, 
	1605, 1613, 1620, 1627, 1634, 1641, 1648, 1656, 1663, 1670, 1677, 1684, 1691, 
	1699, 1706, 1713, 1720, 1727, 1735, 1742, 1749, 1756, 1763, 1770, 1778, 1785, 
	1792, 1799};

public static double[] psi = {
	-31.22, -31.02, -30.81, -30.60, -30.40, -30.22, -29.98, -29.78, -29.57, 
	-29.36, -29.16, -28.92, -28.71, -28.51, -28.30, -28.09, -27.89, -27.65, 
	-27.44, -27.24, -27.03, -26.82, -26.62, -26.38, -26.17, -25.97, -25.76, 
	-25.55, -25.35, -25.11, -24.90, -24.70, -24.49, -24.28, -24.05, -23.84, 
	-23.63, -23.43, -23.22, -23.01, -22.78, -22.57, -22.36, -22.16, -21.95, 
	-21.74, -21.51, -21.30, -21.09, -20.88, -20.68, -20.44, -20.23, -20.03,
	-19.82, -19.61, -19.41, -19.17, -18.96, -18.76, -18.55, -18.34, -18.14, 
	-17.90, -17.69, -17.49, -17.28, -17.07, -16.87, -16.63, -16.42, -16.22, 
	-16.01, -15.80, -15.57, -15.36, -15.15, -14.95, -14.74, -14.53, -14.30,
	-14.09, -13.88, -13.68, -13.47, -13.26, -13.03, -12.82, -12.61, -12.41,
	-12.20, -11.99, -11.76, -11.55, -11.34, -11.14, -10.93, -10.69, -10.49, 
	-10.28, -10.07, -9.87, -9.66, -9.42, -9.22, -9.01, -8.80, -8.60, -8.39,
	-8.15, -7.95, -7.74, -7.53, -7.33, -7.12, -6.88, -6.68, -6.47, -6.26, 
	-6.06, -5.82, -5.61, -5.41, -5.20, -4.99, -4.79, -4.55, -4.34, -4.14, 
	-3.93, -3.72, -3.52, -3.28, -3.07, -2.87, -2.66, -2.45, -2.22, -2.01, 
	-1.80, -1.60, -1.39, -1.18, -0.95, -0.74, -0.53, -0.32, -0.12, 0.04, 0.16, 
	0.26, 0.36, 0.46, 0.57, 0.67, 0.78, 0.88, 0.99, 1.09, 1.19, 1.31, 1.41,
	1.51, 1.61, 1.71, 1.81, 1.93, 2.03, 2.13, 2.23, 2.33, 2.44, 2.55, 2.65,
	2.76, 2.86, 2.96, 3.06, 3.18, 3.28, 3.38, 3.48, 3.58, 3.70, 3.80, 3.90,
	4.00, 4.10, 4.21, 4.32, 4.42, 4.52, 4.63, 4.73, 4.83, 4.94, 5.05, 5.15,
	5.25, 5.35, 5.47, 5.57, 5.67, 5.77, 5.87, 5.97, 6.09, 6.19, 6.29, 6.39, 
	6.50, 6.60, 6.71, 6.82, 6.92, 7.02, 7.12, 7.22, 7.34, 7.44, 7.54, 7.64, 
	7.74, 7.86, 7.96, 8.06, 8.16, 8.27, 8.37, 8.48, 8.58, 8.69, 8.79, 8.89,
	8.99, 9.11, 9.21, 9.31, 9.41, 9.51, 9.61, 9.73, 9.83, 9.93, 10.03, 10.14,
	10.25, 10.35, 10.45, 10.56, 10.66, 10.76, 10.88, 10.98, 11.08, 11.18};

		public static double[] volts = { 
	0.00, 0.02, 0.04, 0.06, 0.08, 0.10, 0.12, 0.14, 0.16, 0.18, 0.20, 0.22, 0.24,
	0.25, 0.27, 0.29, 0.31, 0.33, 0.35, 0.37, 0.39, 0.41, 0.43, 0.45, 0.47, 0.49,
	0.51, 0.53, 0.55, 0.57, 0.59, 0.61, 0.63, 0.65, 0.67, 0.69, 0.71, 0.73, 0.75,
	0.76, 0.78, 0.80, 0.82, 0.84, 0.86, 0.88, 0.90, 0.92, 0.94, 0.96, 0.98, 1.00,
	1.02, 1.04, 1.06, 1.08, 1.10, 1.12, 1.14, 1.16, 1.18, 1.20, 1.22, 1.24, 1.25,
	1.27, 1.29, 1.31, 1.33, 1.35, 1.37, 1.39, 1.41, 1.43, 1.45, 1.47, 1.49, 1.51,
	1.53, 1.55, 1.57, 1.59, 1.61, 1.63, 1.65, 1.67, 1.69, 1.71, 1.73, 1.75, 1.76,
	1.78, 1.80, 1.82, 1.84, 1.86, 1.88, 1.90, 1.92, 1.94, 1.96, 1.98, 2.00, 2.02,
	2.04, 2.06, 2.08, 2.10, 2.12, 2.14, 2.16, 2.18, 2.20, 2.22, 2.24, 2.25, 2.27,
	2.29, 2.31, 2.33, 2.35, 2.37, 2.39, 2.41, 2.43, 2.45, 2.47, 2.49, 2.51, 2.53,
	2.55, 2.57, 2.59, 2.61, 2.63, 2.65, 2.67, 2.69, 2.71, 2.73, 2.75, 2.76, 2.78,
	2.80, 2.82, 2.84, 2.86, 2.88, 2.90, 2.92, 2.94, 2.96, 2.98, 3.00, 3.02, 3.04,
	3.06, 3.08, 3.10, 3.12, 3.14, 3.16, 3.18, 3.20, 3.22, 3.24, 3.25, 3.27, 3.29,
	3.31, 3.33, 3.35, 3.37, 3.39, 3.41, 3.43, 3.45, 3.47, 3.49, 3.51, 3.53, 3.55,
	3.57, 3.59, 3.61, 3.63, 3.65, 3.67, 3.69, 3.71, 3.73, 3.75, 3.76, 3.78, 3.80,
	3.82, 3.84, 3.86, 3.88, 3.90, 3.92, 3.94, 3.96, 3.98, 4.00, 4.02, 4.04, 4.06,
	4.08, 4.10, 4.12, 4.14, 4.16, 4.18, 4.20, 4.22, 4.24, 4.25, 4.27, 4.29, 4.31,
	4.33, 4.35, 4.37, 4.39, 4.41, 4.43, 4.45, 4.47, 4.49, 4.51, 4.53, 4.55, 4.57,
	4.59, 4.61, 4.63, 4.65, 4.67, 4.69, 4.71, 4.73, 4.75, 4.76, 4.78, 4.80, 4.82,
	4.84, 4.86, 4.88, 4.90, 4.92, 4.94, 4.96, 4.98, 5.00 };

		public static double[] Percent = { 
	0.00, 0.39, 0.78, 1.18, 1.57, 1.96, 2.35, 2.75, 3.14, 
	3.53, 3.92, 4.31, 4.71, 5.10, 5.49, 5.88, 6.27, 6.67, 7.06, 7.45, 7.84,
	8.24, 8.63, 9.02, 9.41, 9.80, 10.20, 10.59, 10.98, 11.37, 11.76, 12.16,
	12.55, 12.94, 13.33, 13.73, 14.12, 14.51, 14.90, 15.29, 15.69, 16.08, 16.47,
	16.86, 17.25, 17.65, 18.04, 18.43, 18.82, 19.22, 19.61, 20.00, 20.39, 20.78,
	21.18, 21.57, 21.96, 22.35, 22.75, 23.14, 23.53, 23.92, 24.31, 24.71, 25.10,
	25.49, 25.88, 26.27, 26.67, 27.06, 27.45, 27.84, 28.24, 28.63, 29.02, 29.41,
	29.80, 30.20, 30.59, 30.98, 31.37, 31.76, 32.16, 32.55, 32.94, 33.33, 33.73,
	34.12, 34.51, 34.90, 35.29, 35.69, 36.08, 36.47, 36.86, 37.25, 37.65, 38.04,
	38.43, 38.82, 39.22, 39.61, 40.00, 40.39, 40.78, 41.18, 41.57, 41.96, 42.35,
	42.75, 43.14, 43.53, 43.92, 44.31, 44.71, 45.10, 45.49, 45.88, 46.27, 46.67,
	47.06, 47.45, 47.84, 48.24, 48.63, 49.02, 49.41, 49.80, 50.20, 50.59, 50.98,
	51.37, 51.76, 52.16, 52.55, 52.94, 53.33, 53.73, 54.12, 54.51, 54.90, 55.29,
	55.69, 56.08, 56.47, 56.86, 57.25, 57.65, 58.04, 58.43, 58.82, 59.22, 59.61,
	60.00, 60.39, 60.78, 61.18, 61.57, 61.96, 62.35, 62.75, 63.14, 63.53, 63.92,
	64.31, 64.71, 65.10, 65.49, 65.88, 66.27, 66.67, 67.06, 67.45, 67.84, 68.24,
	68.63, 69.02, 69.41, 69.80, 70.20, 70.59, 70.98, 71.37, 71.76, 72.16, 72.55,
	72.94, 73.33, 73.73, 74.12, 74.51, 74.90, 75.29, 75.69, 76.08, 76.47, 76.86,
	77.25, 77.65, 78.04, 78.43, 78.82, 79.22, 79.61, 80.00, 80.39, 80.78, 81.18,
	81.57, 81.96, 82.35, 82.75, 83.14, 83.53, 83.92, 84.31, 84.71, 85.10, 85.49,
	85.88, 86.27, 86.67, 87.06, 87.45, 87.84, 88.24, 88.63, 89.02, 89.41, 89.80, 
	90.20, 90.59, 90.98, 91.37, 91.76, 92.16, 92.55, 92.94, 93.33, 93.73, 94.12, 
	94.51, 94.90, 95.29, 95.69, 96.08, 96.47, 96.86, 97.25, 97.65, 98.04, 98.43, 
	98.82, 99.22, 99.61, 100.00 };

		public static double[] celsius = { 140.13, 137.63, 135.19, 132.82, 130.50, 128.25, 126.05,
	123.91, 121.82, 119.79, 117.81, 115.88, 114.00, 112.17, 110.38, 108.64, 
	106.94, 105.28, 103.67, 102.10, 100.56, 99.07, 97.61, 96.18, 94.79, 93.44, 
	92.11, 90.82, 89.56, 88.33, 87.13, 85.96, 84.81, 83.69, 82.60, 81.53, 80.48,
	79.46, 78.46, 77.48, 76.53, 75.59, 74.67, 73.77, 72.89, 72.03, 71.19, 70.36,
	69.55, 68.75, 67.97, 67.20, 66.45, 65.71, 64.99, 64.27, 63.57, 62.88, 62.20,
	61.54, 60.88, 60.23, 59.60, 58.97, 58.35, 57.74, 57.14, 56.55, 55.96, 55.39,
	54.82, 54.25, 53.70, 53.15, 52.61, 52.07, 51.54, 51.01, 50.49, 49.98, 49.47,
	48.96, 48.46, 47.97, 47.48, 46.99, 46.51, 46.03, 45.55, 45.08, 44.61, 44.15,
	43.69, 43.23, 42.77, 42.32, 41.87, 41.43, 40.98, 40.54, 40.10, 39.66, 39.23,
	38.80, 38.37, 37.94, 37.51, 37.09, 36.67, 36.25, 35.83, 35.41, 35.00, 34.59,
	34.17, 33.76, 33.35, 32.95, 32.54, 32.14, 31.73, 31.33, 30.93, 30.53, 30.13,
	29.73, 29.34, 28.94, 28.54, 28.15, 27.76, 27.36, 26.97, 26.58, 26.19, 25.80,
	25.41, 25.02, 24.63, 24.25, 23.86, 23.47, 23.08, 22.70, 22.31, 21.92, 21.54, 
	21.15, 20.76, 20.38, 19.99, 19.60, 19.22, 18.83, 18.44, 18.05, 17.67, 17.28,
	16.89, 16.50, 16.11, 15.71, 15.32, 14.93, 14.53, 14.14, 13.74, 13.34, 12.94,
	12.54, 12.14, 11.74, 11.34, 10.93, 10.52, 10.11, 9.70, 9.29, 8.87, 8.46, 
	8.04, 7.62, 7.20, 6.77, 6.35, 5.92, 5.49, 5.05, 4.62, 4.18, 3.74, 3.29, 2.85,
	2.40, 1.94, 1.49, 1.03, 0.57, 0.11, -0.36, -0.82, -1.30, -1.77, -2.25, -2.73,
	-3.21, -3.70, -4.19, -4.68, -5.17, -5.67, -6.17, -6.68, -7.18, -7.69, -8.21,
	-8.72, -9.24, -9.76, -10.28, -10.81, -11.33, -11.86, -12.40, -12.93, -13.47,
	-14.00, -14.54, -15.09, -15.63, -16.17, -16.72, -17.26, -17.81, -18.36,
	-18.90, -19.45, -20.00, -20.55, -21.09, -21.64, -22.18, -22.72, -23.26,
	-23.80, -24.34, -24.87, -25.40, -25.93, -26.45, -26.97, -27.49, -27.99,
	-28.50, -28.99, -29.48};

		#endregion

		public static double FormulaInjTimeCalc(int value, bool InverseBytes)
		{
			// Время форсунки
			// word value = ((int)hi<<8) + lo 
			//InjTime = 3.2 *(Word)value // 1000 У доктроника
			//Injtime=(time_h*100)+(time_l*100/255);// У Starick, не пойму я ее
			//Injtime= (value / 400d) + 0.6d; // Где-то в исходниках видел
			if (InverseBytes)
			{
				value = ((value << 8) & 0xFF00) | ((value >> 8) & 0xFF);
			}
			//double res = 3.2d * value / 1000;
			double res = (((value >> 8) * 100d) + ((value & 0xff) * 100 / 255d))/100;
			return res;
		}
		public static double FormulaIngAdv1FAngle(byte value)
		{
			// Угол опережения
			return (value - 24L) / 4d;
			//return (double)value / 32832;
		}
		
		public static double FormulaO2StockAFR(byte iValue)
		{
			double num2 = volts[iValue];
			if (num2 < 0.05)
			{
				num2 = 19.1;
			}
			if ((num2 >= 0.05) & (num2 <= 0.249))
			{
				num2 =  Math.Round(19.1 - (4.4 * (num2 / 0.249)), 2);
			}
			if ((num2 >= 0.25) & (num2 <= 0.77))
			{
				num2 = Math.Round(14.7 - (0.1 * (num2 / 0.749)), 2);
			}
			if ((num2 > 0.77) & (num2 < 1.0))
			{
				num2 = Math.Round((10.3 * (1.0 / num2)) + 0.5, 2);
			}
			if (num2 >= 1.0)
			{
				return 10.3;
			}
			return num2;
		}
		public static double FormulaO2StockLambda(byte value)
		{
			return volts[value];
		}
		public static double FormulaLAFSensor(byte value)
		{
			return 0.289*Math.Pow(value,4) + 2.159*Math.Pow(value,3) + 6.081*Math.Pow(value,2) + 7.389*value;
		}

		public static double FormulaMAPInBar(byte value)
		{
			//if (MAP)
			//{
			//	temp = (MAP * 7.626) - 148.252;
			//	Data = 1013.25 - temp;
			//}
			return Math.Round((6.91764705882353 * value) + 25.0, 0);
		}
		public static double FormulaBaroAPInBar(byte value)
		{
			double X = (double)value * 5 / 256; // 5v / 255 bits = sensor range
			return ((3.6527 * X * X) + (20.349 * X) + 11.236) * 33.42;
			
		}
		public static double FormulaMAPInPSI(double buffer)
		{
			return Math.Round((buffer * 0.0145038) - 14.5038, 2);
		}
		public static string FormulaMAPPSIHEX(double buffer)
		{
			long num = (long)Math.Round(((((((buffer / 14.7) * 1000.0) + 1000.0) / 364.0) + 0.125) / 5.0) * 255.0);
			return num.ToString("X");
		}
		public static double FormulaTPS(byte value)
		{
			int tvalue =value- 25;
			if(tvalue<0)
			{
				int i = 0;
			}
			return tvalue > 0 ? 
				Percent[tvalue] : 
				-Percent[-tvalue];
		}
		public static double FormulaBAT(byte value)
		{
			return (value*0.05)+6.5;
		}
		public static int FormulaRPM(int Value, RPMCalcMethod method, bool InverseBytes)
		{
			int res = 0;
			if (Value != 0)
			{
				if(InverseBytes)
				{
					Value = ((Value<<8)& 0xFF00) | ((Value>>8)&0xFF); 
				}
				switch (method)
				{
					case RPMCalcMethod.Obd1:
						res = (int) ((double) 1875000 / Value);
						break;
					case RPMCalcMethod.Obd0:
						res = (int) ((double) 1848000 / Value);
						break;
					case RPMCalcMethod.Obd2a:
						res = (int) (Value /4f);
						break;
					default:
						res = (int) ((double) 1920000 / Value);
						break;
				}
			}
			return res;
		}
		public static double Formula_CT_LT_Correction(byte value)
		{
			double res = 0;
			if (value == 0)
				value = 128;
			res = ((double) value / 128 - 1)*100;
			return res;
		}
		public static double FormulaDutyCycle(double RPM, double InjTimeIn_ms)
		{
			return RPM / (60 * 2) * (InjTimeIn_ms / 1000) * 100;
		}
		public static double FormulaTempInCelsius(byte value)
		{
			return celsius[value];
		}
		#region Error Codes
		public class Errors
		{
			public static string[] ErrorCodesDescription = 
			{
				"00 ECU Defective ECU",
				"01 Primary O2 sensor Open or short in O2 sensor circuit, defective Primary O2 sensor",
				"02 Secondary O2 sensor Open or short in O2 sensor circuit, defective Secondary O2 sensor",
				"03 Manifold Absolute Pressure sensor Open or short in MAP sensor circuit, defective MAP sesnor",
				"04 Crankshaft Position sensor Timing belt slipped, defective CP sensor",
				"05 Manifold Absolute Pressure sensor Reading too low or too high, defective MAP sensor",
				"06 Engine Coolant Temp circuit Reading too low or too high, open or short in circuit, defective ECT circuit",
				"07 Throttle Position circuit Reading too low or too high, open or short in circuit, defective TPS circuit",
				"08 Top Dead Center sensor Open or short in TDC sensor circuit, defective TDC sensor",
				"09 Cylinder Position sensor Open or short in CYP sensor circuit, defective CYP sensor",
				"10 Intake Air Temperature sensor Open or short in IAT sensor circuit, defective IAT sensor",
				"11 - ?",
				"12 Exhaust Gas Recirculation Valve Open or short in EGR valve circuit, defective EGR valve",
				"13 Barometric Pressure sensor circuit Defective ECM (Barometric sensor)",
				"14 Idle Control System Malfunction Defective IAC valve, defective fast idle thermo valve, throttle body",
				"15 Ignition Output Signal Missing, defective or noisy Ignition Output Signal",
				"16 Fuel Injector System Defective or malfunctioning Fuel Injector System",
				"17 Vehicle Speed sensor circuit Open or short in VS sensor circuit, defective VS sensor",
				"18 - ?",
				"19 Lock-up Control Solenoid Open or short in LCS circuit, defective LCS solenoid",
				"20 Electrical Load Detector circuit Open or short in ELD circuit, defective ELD circuit",
				"21 VTEC Solenoid Valve Open or short in VTEC solenoid valve circuit, defective VTEC solenoid valve",
        "22 VTEC Oil Pressure Switch Open or short in VTEC OPS circuit, defective VTEC pressure switch",
        "23 Knock sensor circuit Open or short in Knock sensor circuit, defective Knock sensor circuit",
        "24 - ?",
        "25 - ?",
        "26 - ?",
        "27 - ?",
        "28 - ?",
        "29 - ?",
        "30 A/T FI Signal A high/low input Defective AFSA line, TCM",
        "31 A/T FI Signal B high/low input Defective AFSB line, TCM",
        "32 - ?",
        "33 - ?",
        "34 - ?",
        "35 - ?",
        "36 - ?",
        "37 - ?",
        "38 - ?",
        "39 - ?",
        "40 - ?",
        "41 Primary Heated O2 sensor heater circuit Open or short in Primary O2 sensor heater circuit, defective Primary O2 sensor heater circuit",
        "42 - ?",
        "43 Fuel Supply System Defective or malfunctioning Fuel Supply System",
        "44 Fuel Supply System (Slave)",
        "45 System too lean/rich Fuel supply system, Primary O2 sensor, MAP sensor, contaminated fuel, valve clearance, exhaust leakage",
        "46 System Too Lean or Too Rich (Slave)",
        "47 - ?",
        "48 Lean Air Fuel sensor Open or short in Lean Air Fuel sensor circuit, defective Lean Air Fuel sensor",
        "49 - ?",
        "50 - ?",
        "51 - ?",
        "52 - ?",
        "53 Датчик детонации (второй)",
        "54 Crankshaft Speed Fluctuation sensor Open or short in CKF sensor circuit, defective CKF sensor circuit",
        "55 - ?",
        "56 - ?",
        "57 - ?",
        "58 Top Dead Center sensor #2 Open or short in TDC sensor circuit, defective TDC sensor",
        "59 CYP Sensor (Cylinder)",
        "60 - ?",
        "61 Primary Heated O2 sensor Slow response, exhaust leak, defective Primary Heated O2 sensor",
        "62 - ?",
        "63 Secondary Heated O2 sensor Slow response, exhaust leak, defective Secondary Heated O2 sensor",

			};
			/*
			65 Secondary Heated O2 sensor circuit Open or short in Secondary Heated O2 sensor circuit, defective Secondary Heated O2 sensor circuit
			OBD-II ONLY (1996-Present)
			67 Catalyst System Efficiency System below threshold, defective catalytic converter
			70 Automatic Transaxle A/T transmission, A/T FI data line, TCM, ECM
			71 Random Misfire (Cylinder 1) Ignition system, fuel supply system, MAP sensor, IAC valve, contaminated fuel, lack of fuel, fuel injector,fuel injector circuit, low compression, valve clearance
			72 Random Misfire (Cylinder 2)
			73 Random Misfire (Cylinder 3)
			74 Random Misfire (Cylinder 4)
			75 Random Misfire (Cylinder 5)
			76 Random Misfire (Cylinder 6)
			80 Exhaust Gas Re-circulation Malfunction with EGR system, insufficient flow
			86 Engine Coolant Temp circuit Defective ECT sensor, defective cooling system
			90 Evaporative Emission Control System Fuel cap loose, poor vacuum connection, fuel tank, fuel tank pressure sensor, EVAP bypass solenoid valve,EVAP tow-way valve, EVAP control canister vent shut valve, EVAP canister, EVAP purge control solenoid valve
			91 Fuel Tank Pressure sensor circuit Possible leak in the Fuel Tank Emissions System, open or short in FTP sensor circuit, defective FTP sensor circuit
			92 Evaporative Emission Control System Insufficient Purge Flow or Defective Emissions System		
					 * */
		}
		#endregion
	}
	public enum ValuesOffset : byte
	{
		RPM = 0x00, // обороты (2 байта)
		VSS = 0x02, // скорость
		Flag1 = 0x08, //15 0-SW Starter 1-SW AC 3-SW Brake
		Flag2 = 0x09, //16 3- SCS самодиагностика
		Flag3 = 0x0A, //17 2- VTS Vtec Control
		Flag4 = 0x0B, //18 0-MainRelay 2-O2Heater 5-Engine
		Flag5 = 0x0C, //19 3-VTEC E 7-Econo
		Flag6 = 0x0D, //20
		Flag7 = 0x0E, //21
		Flag8 = 0x0F, //22
		ECT = 0x10, // темп антифриза
		IAT = 0x11, // температура воздуха
		MAP = 0x12, // давление во впуске
		PA = 0x13, // давление атмосферы
		TPS = 0x14, // датчик дросс заслонки
		O2 =	0x15, // лямбда или датчик обедненной смеси
		Bat = 0x17, // напряжение борт сети
		ALTF = 0x18, // Alternator FR Signal (ALTF) Сигнал контроля бортового напряжения
		CorrCT = 0x20, // крастковременная топл коррекция
		CorrLT = 0x22, // долговременная топл коррекция
		Inj = 0x24,    //  (2 байта) время открытия форсунки, по ней я считал мгновенный расход топлива
		IngAdv1 = 0x26,// это Угол ОЗ
		IngAdv2 = 0x27,// что-то с зажиганием
		IAC = 0x29, // The Idle air control valve, or IACV regulates the car's idle based on the coolant temperature.

		Knock = 0x3C, // Датчик детонации

		Errors1 = 0x40,// Парсятся по пол-байта, если пол-байта не равны 0, то значит
		Errors2 = 0x50,// есть такой код ошибки. Номер ошибки - смещение полубайта. смещение начинается от 0

		VSS2 = 0x61, // скорость
		RPM2 = 0x62, // обороты (2 байта)
		ECT2 = 0x64, // темп антифриза
		IAT2 = 0x65, // температура воздуха
		MAP2 = 0x66, // давление во впуске
		PA2 = 0x67, // давление атмосферы
		TPS2 = 0x68, // датчик дросс заслонуи		
		Bat2 = 0x69, // напряжение борт сети

		O22 = 0x6C, // лямбда или датчик обедненной смеси
		CorrCT2 = 0x6D, // крастковременная топл коррекция
		IAC2 = 0x6E, // The Idle air control valve, or IACV regulates the car's idle based on the coolant temperature.
		
		ECUID = 0x76 // ID ECU
	}
	[Flags]
	public enum HOBDFlags : ulong
	{
		AllOff = 0,
		Starter = (ulong)1 << 0,
		AirCondition = (ulong)1 << 1,
		PASPressure = (ulong)1 << 2,
		VTECPressure = (ulong)1 << 7,
		Brake = (ulong)1 << 3,
		SCS = (ulong)1 << (3 + 8), //самодиагностика
		VTSControl = (ulong)1 << (2 + 16),
		MainRelay = (ulong)1 << (0 + 24),
		O2Heater = (ulong)1 << (2 + 24),
		CEL = (ulong)1 << (5 + 24),
		Engine = (ulong)1 << (5 + 24),
		VTEC_E = (ulong)1 << (3 + 32),
		Econo = (ulong)1 << (7 + 32),

		ACClutch = (ulong)1 << (1 + 24),
		O2Heater2 = (ulong)1 << (6 + 24),
		FAN = (ulong)1 << (1 + 32),
		AllOn = Starter | AirCondition | PASPressure | VTECPressure | Brake | SCS | VTSControl
		| MainRelay | O2Heater | CEL | Engine | VTEC_E | Econo | ACClutch | O2Heater2 | FAN
	}

	public enum OBDAnswerItemType : uint
	{
		UnknownAnswer,
		RPM,		
		VSS,

		Flags,

		ECT,
		IAT,
		MAP,
		PA,
		TPS,
		O2,
		Bat,
		ALTF,

		CorrCT,
		CorrLT,
		InjTime,
		InjAdv1_FAngle,
		InjAdv2,
		IAC,

		Knock,

		Errors,

		ClearErrors,

		IDECU
	}

	public class OBDAnswerItem
	{
		public OBDAnswerItemType AnsType = OBDAnswerItemType.UnknownAnswer;
		public ValuesOffset Offset;
		public int AnsTime = Environment.TickCount;
		public int Value;
		public byte[] ValuesArray;
		public bool[] ParsedErrorsArray;
		public string Error;

		public override string ToString()
		{
			string res = AnsType.ToString();
			if(ValuesArray != null)
				res += " " + OBDHelper.GetHexString(ValuesArray);
			else
				res += " " + Value;
			if (Error != null)
				res += " " + Error;
			return res;
		}

		public string ToLine()
		{
			StringBuilder sb = new StringBuilder();
			sb.Append(((uint) AnsType).ToString("00")).Append("~");
			sb.Append(((byte) Offset).ToString("X2")).Append("~");
			sb.Append(Value).Append("~");
			sb.Append(AnsTime).Append("~");
			sb.Append(OBDHelper.GetHexString(ValuesArray)).Append("~");
			sb.Append(Error);
			return sb.ToString();
		}
		public static OBDAnswerItem FromLine(string line)
		{
			OBDAnswerItem res = null;
			if(!string.IsNullOrEmpty(line))
			{
				string[] parts = line.Split('~');
				if(parts.Length >= 6)
				{
					try
					{
						res = new OBDAnswerItem
						      	{
						      		AnsType = (OBDAnswerItemType) uint.Parse(parts[0]),
						      		Offset = (ValuesOffset) byte.Parse(parts[1], NumberStyles.AllowHexSpecifier),
						      		Value = int.Parse(parts[2]),
						      		AnsTime = int.Parse(parts[3]),
						      		ValuesArray = OBDHelper.GetBytesFromHexString(parts[4])
						      	};
						if (!string.IsNullOrEmpty(parts[5]))
							res.Error = parts[5];
					}catch
					{
						res = null;
					}
				}
			}
			return res;
		}
	}

	public class OBDCommand
	{
		public int TimeOut = 2000;
		public int SendTime;
		public int ReceiveAnswerTime;
		public byte[] SendBytes;
		public byte[] ReceivedBytes = new byte[0];
		public byte[] ReceivedDataBytes = new byte[0];
		public byte StartTableOffset;
		public bool LastCommandInCommandChain;
		public readonly ThreadSafeList<OBDAnswerItem> AnsList = new ThreadSafeList<OBDAnswerItem>();

		public override string ToString()
		{
			string res = ((ValuesOffset) StartTableOffset).ToString();
			if(AnsList != null)
			{
				for(int i=0; i<AnsList.Count; i++)
				{
					res += ": " + AnsList[i];
				}
			}
			if (SendBytes != null && SendBytes.Length == 4)
				if (SendBytes[0] == 0x21 && SendBytes[1] == 0x04 && SendBytes[2] == 0x03 && SendBytes[3] == 0xD8)
					res = "Clear errors command";
			return res;
		}

		public bool IsTimeout()
		{
			return Math.Abs(Environment.TickCount - SendTime) > TimeOut;
		}
		public virtual bool ParseAnswer(byte[] InData)
		{
			bool res = true;
			try
			{
				int indexOffset = StartTableOffset;
				BinaryReader br = new BinaryReader(new MemoryStream(InData));
				{
					while(br.BaseStream.Length>br.BaseStream.Position)
					{
						OBDAnswerItem Ans = new OBDAnswerItem();
						try
						{
							Ans.Offset = (ValuesOffset) indexOffset;
							Ans.AnsTime = ReceiveAnswerTime;
							switch (Ans.Offset)
							{
								case ValuesOffset.RPM:
								case ValuesOffset.RPM2:
									Ans.AnsType = OBDAnswerItemType.RPM;
									break;
								case ValuesOffset.VSS:
								case ValuesOffset.VSS2:
									Ans.AnsType = OBDAnswerItemType.VSS;
									break;
								case ValuesOffset.Flag1:
								case ValuesOffset.Flag2:
								case ValuesOffset.Flag3:
								case ValuesOffset.Flag4:
								case ValuesOffset.Flag5:
								case ValuesOffset.Flag6:
								case ValuesOffset.Flag7:
								case ValuesOffset.Flag8:
									Ans.AnsType = OBDAnswerItemType.Flags;
									break;
								case ValuesOffset.ECT:
								case ValuesOffset.ECT2:
									Ans.AnsType = OBDAnswerItemType.ECT;
									break;
								case ValuesOffset.IAT:
								case ValuesOffset.IAT2:
									Ans.AnsType = OBDAnswerItemType.IAT;
									break;
								case ValuesOffset.MAP:
								case ValuesOffset.MAP2:
									Ans.AnsType = OBDAnswerItemType.MAP;
									break;
								case ValuesOffset.PA:
								case ValuesOffset.PA2:
									Ans.AnsType = OBDAnswerItemType.PA;
									break;
								case ValuesOffset.TPS:
								case ValuesOffset.TPS2:
									Ans.AnsType = OBDAnswerItemType.TPS;
									break;
								case ValuesOffset.O2:
								case ValuesOffset.O22:
									Ans.AnsType = OBDAnswerItemType.O2;
									break;
								case ValuesOffset.Bat:
								case ValuesOffset.Bat2:
									Ans.AnsType = OBDAnswerItemType.Bat;
									break;
								case ValuesOffset.ALTF:
									Ans.AnsType = OBDAnswerItemType.ALTF;
									break;
								case ValuesOffset.CorrCT:
								case ValuesOffset.CorrCT2:
									Ans.AnsType = OBDAnswerItemType.CorrCT;
									break;
								case ValuesOffset.CorrLT:
									Ans.AnsType = OBDAnswerItemType.CorrLT;
									break;
								case ValuesOffset.Inj:
									Ans.AnsType = OBDAnswerItemType.InjTime;
									break;
								case ValuesOffset.IngAdv1:
									Ans.AnsType = OBDAnswerItemType.InjAdv1_FAngle;
									break;
								case ValuesOffset.IngAdv2:
									Ans.AnsType = OBDAnswerItemType.InjAdv2;
									break;
								case ValuesOffset.IAC:
								case ValuesOffset.IAC2:
									Ans.AnsType = OBDAnswerItemType.IAC;
									break;
								case ValuesOffset.Knock:
									Ans.AnsType = OBDAnswerItemType.Knock;
									break;
								case ValuesOffset.Errors1:
								case ValuesOffset.Errors2:
									Ans.AnsType = OBDAnswerItemType.Errors;
									break;
								case ValuesOffset.ECUID:
									Ans.AnsType = OBDAnswerItemType.IDECU;
									break;
							}
							byte len = OBDHelper.GetLenOBDSensorAnswer(Ans.Offset);
							switch(len)
							{
								case 1:
									Ans.Value = br.ReadByte();
									break;
								case 2:
									Ans.Value = br.ReadUInt16();
									break;
								default:
									Ans.ValuesArray = br.ReadBytes(len);
									break;
							}
							indexOffset += len;
							if (Ans.AnsType == OBDAnswerItemType.Errors)
							{
								Ans.ParsedErrorsArray = new bool[64];
								for (int i = 0; i < Ans.ValuesArray.Length; i++)
								{
									byte index = (byte) (((byte)Ans.Offset - 0x40) + i * 2);
									Ans.ParsedErrorsArray[index] = (Ans.ValuesArray[i] & 0x0F) != 0;
									Ans.ParsedErrorsArray[index+1] = (Ans.ValuesArray[i] & 0xF0) != 0;
								}
							}
						}catch(Exception ex)
						{
							Ans.Error = ex.Message;
						}
						AnsList.Add(Ans);
					}
				}
			}
			catch {
				res = false; }
			return res;
		}

		public string ToLine()
		{
			string res = null;
			if(SendBytes != null && ReceivedBytes != null)
			{
				StringBuilder sb = new StringBuilder();
				sb.Append(ReceiveAnswerTime).Append("~");
				sb.Append(OBDHelper.GetHexString(SendBytes)).Append("~");
				sb.Append(OBDHelper.GetHexString(ReceivedDataBytes)).Append("~");
				res = sb.ToString();
			}
			return res;
		}
		public static OBDCommand FromLine(string line)
		{
			OBDCommand res = null;
			if (!string.IsNullOrEmpty(line))
			{
				string[] parts = line.Split('~');
				if (parts.Length >= 3)
				{
					try
					{
						res = new OBDCommand
						      	{
						      		ReceiveAnswerTime = int.Parse(parts[0]),
						      		SendBytes = OBDHelper.GetBytesFromHexString(parts[1]),
						      		ReceivedDataBytes = OBDHelper.GetBytesFromHexString(parts[2])
						      	};

						res.StartTableOffset = res.SendBytes[2];
					}
					catch
					{
						res = null;
					}
				}
			}
			return res;
			
		}
	}
//  x = data * (5 / 256)
//  ECT = (((0.1423 * x ^ 6) - (2.4938 * x ^ 5) + (17.837 * x ^ 4) - (68.698 * x ^ 3) + (154.69 * x ^ 2) - (232.75 * x) + 291.24) - 32) * (5 / 9)
//  IAT = ECT
//  O2 = (0-0v; 50-1v)
//	If data <= 141 Then
//    MAP = 0 - ((-0.1957 * data + 27.587) / 1.875)
//    frmDatalog.DefInstance.lMAP.Text = "in/Hg"
//    frmDatalog.DefInstance.tMAP.Text = CStr(MAP)
//    frmDgauges.DefInstance.mapLED.Value = MAP
//    frmDgauges.DefInstance.lblMAP.Text = "in/Hg"
//    frmAgauges.DefInstance.mapg.Value = CDbl(MAP)
//	Else
//    MAP = 0 - ((0.1124 * data - 15.843) / 1.875)
//    frmDatalog.DefInstance.lMAP.Text = "PSI"
//    frmDatalog.DefInstance.tMAP.Text = CStr(MAP)
//    frmDgauges.DefInstance.mapLED.Value = MAP
//    frmDgauges.DefInstance.lblMAP.Text = "PSI"
//    frmAgauges.DefInstance.mapg.Value = CDbl(MAP)
//	End If
//	Case 3: temp = DataValue * 2.036 'PSI to InHg
//	Case 4: temp = DataValue * 0.4912 'InHg to PSI
//	Case 5: temp = DataValue * 0.02992 'MBar to InHg
//	Case 6: temp = DataValue * 33.42  'InHg to MBar
//	Case 7: temp = DataValue * 0.0002953 'KPA to InHg
//	Case 8: temp = DataValue * 3.3838 'InHg to KPA
//  TPS
//  If data > 250 Then '250 = 4.49v, 4.49v = WOT)
//    data = 250
//	End If
//	If data < 47 Then  '47 = Closed Throttle
//    data = 47
//	End If
//	TPS = ((0.4926 * data) - 23.153)

//  frmDatalog.DefInstance.tRPM.Text = CStr(rpm)
//  VSS = data
//  frmDatalog.DefInstance.tVSS.Text = CStr(VSS)

//Public Function CalcBaro(baro As Integer) As String 'this is estimated currently using values I believe to be accurate
//On Error GoTo errorhandler
//    Const m = -1.5
//    Const B = 27
//        CalcBaro = m * CInt(baro) + B
//            Exit Function
//errorhandler:
//    MsgBox ("Error Intercepted Calculating Baro Pressure!" & vbCrLf & Err.Description & ", " & Err.Number)

//End Function
	// Baro mBar
				//X = fBuffer * 5 / 256; // 5v / 255 bits = sensor range
	//        TempF = ((3.6527 * X * X) + (20.349 * X) + 11.236) * 33.42;

//  Listed below is honda's table for appropriate knock noise @ WOT
//__RPM 	Knock Sensor __Voltage(Volts)
//0 	0.01953125
//800 	0.01953125
//1600 	0.01953125
//2400 	0.05859375
//3200 	0.29296875
//4000 	0.7421875
//4800 	1.23046875
//5600 	1.71875
//6400 	2.2265625
//7200 	2.71484375
//8000 	3.22265625
//8800 	3.7109375
//9600 	4.140625
//10400 	4.609375
//11200 	4.98046875
//12000 	4.98046875
//12800 	4.98046875
	public delegate void OnNewOBDAnswerDelegate(OBDCommand cmd);
	public class OBDLinker : Linker
	{
		public OnNewOBDAnswerDelegate OnAnswer;
		public LogStorage Log = new LogStorage();
		public ThreadSafeQueue<OBDCommand> CommandsQueue = new ThreadSafeQueue<OBDCommand>();		
		public int LastCommandChainSpan =-1;

		private readonly Thread UpdateThread;
		private int LastCommandChainEndTime = -1;
		private OBDCommand fCurCommand;
		
		private OBDCommand CurrCommand
		{
			get { return fCurCommand; }
			set
			{
				if(fCurCommand != null && value == null)
				{
					if(fCurCommand.LastCommandInCommandChain)
					{
						if (LastCommandChainEndTime != -1)
						{
							int newSpan = Math.Abs(Environment.TickCount - LastCommandChainEndTime);
							if (LastCommandChainSpan == -1)
								LastCommandChainSpan = newSpan;
							else
							{
								int difference = (newSpan - LastCommandChainSpan) / 10;
								LastCommandChainSpan += difference; 
							}
						}
						LastCommandChainEndTime = Environment.TickCount;
					}
				}
				fCurCommand = value;
			}
		}

		public OBDLinker(int PortNum) : base(PortNum)
		{
			UpdateThread = new Thread(UpdateThreadProc) {IsBackground = true};
			UpdateThread.Start();
		}
		public OBDLinker()
		{
			
		}

		public void SendLogData(byte[] logPreambula ,byte[] logData, int logTime)
		{
			if(logData != null && logPreambula != null)
			{
				CurrCommand = new OBDCommand {SendTime = logTime, SendBytes = logPreambula, StartTableOffset = logPreambula[2]};
				OnReceiveData(logData);
			}
		}

		private void Decode(ref byte[] data)
		{
			if (data != null && data.Length > 0)
			{
				if (CurrCommand != null)
				{
					// Тип сообщения
					if (CurrCommand.ReceivedBytes.Length == 0)
					{
						CurrCommand.ReceivedBytes = new byte[1];
						CurrCommand.ReceivedBytes[0] = data[0];
						data = OBDHelper.DecLenDataArr(data);
					}
					// Длина сообщения
					if (data.Length > 0)
					{
						if (CurrCommand.ReceivedBytes.Length == 1)
						{
							Array.Resize(ref CurrCommand.ReceivedBytes, 2);
							CurrCommand.ReceivedBytes[1] = data[0];
							data = OBDHelper.DecLenDataArr(data);
						}
					}
					// Тело сообщения
					if (data.Length > 0)
					{
						int cnt = CurrCommand.ReceivedBytes[1];
						while (CurrCommand.ReceivedBytes.Length < cnt && data.Length > 0)
						{
							Array.Resize(ref CurrCommand.ReceivedBytes, CurrCommand.ReceivedBytes.Length + 1);
							CurrCommand.ReceivedBytes[CurrCommand.ReceivedBytes.Length - 1] = data[0];
							Array.Resize(ref CurrCommand.ReceivedDataBytes, CurrCommand.ReceivedDataBytes.Length + 1);
							CurrCommand.ReceivedDataBytes[CurrCommand.ReceivedDataBytes.Length - 1] = data[0];

							data = OBDHelper.DecLenDataArr(data);
						}
					}
				}
			}
		}

		protected override void OnReceiveData(byte[] data)
		{
			base.OnReceiveData(data);
			if(data != null && data.Length >0)
			{
				Log.LogString = "ECU-->PC (Native)" + OBDHelper.GetHexString(data);
				if (CurrCommand != null)
				{
					while (data.Length > 0)
					{
						Decode(ref data);
						// Типа собрали все сообщение
						if (CurrCommand.ReceivedBytes != null && CurrCommand.ReceivedBytes.Length > 2)
						{
							// Типа получили полнный ответ
							if (CurrCommand.ReceivedBytes.Length == CurrCommand.ReceivedBytes[1])
							{
								bool SelfAns = false;
								if (CurrCommand.ReceivedBytes.Length == CurrCommand.SendBytes.Length)
								{
									SelfAns = true;
									for (int i = 0; i < CurrCommand.SendBytes.Length; i++)
									{
										if (CurrCommand.SendBytes[i] != CurrCommand.ReceivedBytes[i])
											SelfAns = false;
									}
								}
								if (SelfAns)
								{
									CurrCommand.StartTableOffset = CurrCommand.ReceivedBytes[2];
									CurrCommand.ReceivedBytes = new byte[0];
									CurrCommand.ReceivedDataBytes = new byte[0];
									continue;
								}
								CurrCommand.ReceiveAnswerTime = Environment.TickCount;
								// Проверка CRC
								if (OBDHelper.GetCheckSum(CurrCommand.ReceivedBytes, true)
								    == CurrCommand.ReceivedBytes[CurrCommand.ReceivedBytes.Length - 1])
								{
									Array.Resize(ref CurrCommand.ReceivedDataBytes, CurrCommand.ReceivedDataBytes.Length - 1);
									bool Parse0X20 = CurrCommand.SendBytes[0] == 0x20;
									bool Parsed = false;
									if (Parse0X20)
										Parsed = CurrCommand.ParseAnswer(CurrCommand.ReceivedDataBytes);
									else
										if (CurrCommand.ToString() == "Clear errors command")
										{
											if(CurrCommand.ReceivedBytes.Length == 3)
											{
												if (CurrCommand.ReceivedBytes[0] == 0x01 && CurrCommand.ReceivedBytes[1] == 0x03 && CurrCommand.ReceivedBytes[2] == 0xFC)
													Parsed = true;
											}
										}
									if (Parsed)
									{
										if (OnAnswer != null)
											OnAnswer(CurrCommand);
										Log.LogString = "ECU-->PC" + OBDHelper.GetHexString(CurrCommand.ReceivedBytes);
										Log.LogString = CurrCommand.ToString();
										CurrCommand = null;
										break;
									}
									Log.ErrorString = "Unpack ERR: " + CurrCommand.StartTableOffset + " =" +
									                  OBDHelper.GetHexString(CurrCommand.ReceivedBytes);
									CurrCommand = null;
									break;
								}
								Log.ErrorString = "Error CRC: " + CurrCommand.StartTableOffset + " =" +
								                  OBDHelper.GetHexString(CurrCommand.ReceivedBytes);
								CurrCommand = null;
								break;
							}
						}
					}
				}
				else
				{
					Log.color = Color.Purple;
					Log.LogString = "ECU-->PC(Unk Ans):" + OBDHelper.GetHexString(data);
				}
			}
		}

		private void UpdateThreadProc()
		{
			while(P!=null && P.isOpen)
			{
				Update();
				int t = Environment.TickCount;
				if (CurrCommand == null && CommandsQueue.Count > 0)
				{
					CurrCommand = CommandsQueue.Dequeue();
					CurrCommand.SendTime = t;
					SendData(CurrCommand.SendBytes);
					Log.color = Color.Red;
					Log.LogString = "PC-->ECU:" + OBDHelper.GetHexString(CurrCommand.SendBytes);
					Log.color = Color.Black;
				}
				else
				{
					if (CurrCommand != null)
					{
						if (CurrCommand.IsTimeout())
						{
							if(CurrCommand.SendBytes[0] == 0x20)
								Log.ErrorString = "Timeout: " + CurrCommand.StartTableOffset;
							else
								if(CurrCommand.SendBytes[0] == 21)
									Log.ErrorString = "Timeout: CLEAR ERRORS";
							CurrCommand = null;
						}
					}
				}
				Thread.Sleep(10);
			}
		}

		#region ECU Requests
		public void SendO2Req()
		{
			if (CommandsQueue.Count < 2)
			{
				OBDCommand c = new OBDCommand
				               	{
				               		StartTableOffset = (byte) ValuesOffset.O2,
				               		SendBytes = OBDHelper.GetSendBytesFor20H((byte) ValuesOffset.O2, 1)
				               	};
				CommandsQueue.Enqueue(c);
			}
		}

		public void SendIdECUReq()
		{
			OBDCommand c = new OBDCommand
			{
				StartTableOffset = (byte)ValuesOffset.ECUID,
				SendBytes = OBDHelper.GetSendBytesFor20H((byte)ValuesOffset.ECUID, 10)
			};
			CommandsQueue.Enqueue(c);			
		}

		public void SendAllSensorsReq()
		{
			if (CommandsQueue.Count < 2)
			{
				OBDCommand c = new OBDCommand
				{
					StartTableOffset = (byte)ValuesOffset.RPM,
					SendBytes = OBDHelper.GetSendBytesFor20H((byte)ValuesOffset.RPM, 0x10)
				};
				CommandsQueue.Enqueue(c);
				c = new OBDCommand
				{
					StartTableOffset = (byte)ValuesOffset.ECT,
					SendBytes = OBDHelper.GetSendBytesFor20H((byte)ValuesOffset.ECT, 0x10)
				};
				CommandsQueue.Enqueue(c);
				c = new OBDCommand
				{
					StartTableOffset = (byte)ValuesOffset.CorrCT,
					SendBytes = OBDHelper.GetSendBytesFor20H((byte)ValuesOffset.CorrCT, 0x10)
				};
				CommandsQueue.Enqueue(c);
				c.LastCommandInCommandChain = true;
			}
		}

		public void SendFastSensorsReq()
		{
			if (CommandsQueue.Count < 2)
			{
				OBDCommand c = new OBDCommand
				{
					StartTableOffset = (byte)ValuesOffset.VSS2,
					SendBytes = OBDHelper.GetSendBytesFor20H((byte)ValuesOffset.VSS2, 0x10)
				};
				CommandsQueue.Enqueue(c);
				c.LastCommandInCommandChain = true;
			}
		}
		public void SendCustomReq(ThreadSafeList<byte[]> customCodes)
		{
			if(customCodes == null)
				return;
			if (CommandsQueue.Count < 2)
			{
				OBDCommand c = null;
				for (int i = 0; i < customCodes.Count; i++)
				{
					if (customCodes[i] != null && customCodes[i].Length == 2)
					{
						c = new OBDCommand
						               	{
						               		StartTableOffset = customCodes[i][0],
															SendBytes = OBDHelper.GetSendBytesFor20H(customCodes[i][0], customCodes[i][1])
						               	};
						CommandsQueue.Enqueue(c);
					}
				}
				if (c != null)
					c.LastCommandInCommandChain = true;
			}
		}

		public void SendErrorCodesReq()
		{
			CommandsQueue.Clear();
			OBDCommand c = new OBDCommand
			               	{
			               		StartTableOffset = (byte) ValuesOffset.Errors1,
			               		SendBytes = OBDHelper.GetSendBytesFor20H((byte) ValuesOffset.Errors1, 0x10)
			               	};
			CommandsQueue.Enqueue(c);
			c = new OBDCommand
			    	{
			    		StartTableOffset = (byte) ValuesOffset.Errors2,
			    		SendBytes = OBDHelper.GetSendBytesFor20H((byte) ValuesOffset.Errors2, 0x10)
			    	};
			CommandsQueue.Enqueue(c);
			c.LastCommandInCommandChain = true;
		}

		public void SendExperimentalReq()
		{
			if (CommandsQueue.Count == 0)
			{
				OBDCommand c = new OBDCommand
				               	{
				               		StartTableOffset = 0x03,
				               		TimeOut = 5000,
				               		SendBytes = new byte[] {0x23, 0x03, 0xDA},
				               	};
				CommandsQueue.Enqueue(c);
				CommandsQueue.Enqueue(c);
				CommandsQueue.Enqueue(c);
				c = new OBDCommand
				    	{
				    		StartTableOffset = 0x03,
				    		TimeOut = 5000,
				    		SendBytes = new byte[] {0x2E, 0x04, 0xF1, 0xDD},
				    	};
				CommandsQueue.Enqueue(c);
				CommandsQueue.Enqueue(c);
				CommandsQueue.Enqueue(c);
				c = new OBDCommand
				    	{
				    		StartTableOffset = 0x03,
				    		TimeOut = 5000,
				    		SendBytes = new byte[] {0x25, 0x04, 0xE2, 0xF5},
				    	};
				CommandsQueue.Enqueue(c);
				CommandsQueue.Enqueue(c);
				CommandsQueue.Enqueue(c);
			}

		}
		public void SendExperimentalReq2()
		{
			if (CommandsQueue.Count == 0)
			{
				OBDCommand c = new OBDCommand
				{
					StartTableOffset = 0x03,
					TimeOut = 5000,
					SendBytes = new byte[] { 0x40, 0x05, 0x78, 0x05, 0x3E },
				};
				CommandsQueue.Enqueue(c);
				c = new OBDCommand
				{
					StartTableOffset = 0x03,
					TimeOut = 5000,
					SendBytes = new byte[] { 0x60, 0x05, 0x70, 0x0C, 0x1F },
				};
				CommandsQueue.Enqueue(c);
				c = new OBDCommand
				{
					StartTableOffset = 0x03,
					TimeOut = 5000,
					SendBytes = new byte[] { 0xA0, 0x05, 0x70, 0x05, 0xE6 },
				};
				CommandsQueue.Enqueue(c);
			}

		}
		public void SendClearECURequest()
		{
			CommandsQueue.Clear();
			OBDCommand c = new OBDCommand
			               	{
			               		StartTableOffset = 0x03,
			               		TimeOut = 5000,
												SendBytes = new byte[] { 0x21, 0x04, 0x03, 0xD8 },
												// Не знаю что за код, но ошибки стирает из PGFMI
												//SendBytes = new byte[] { 0x21, 0x04, 0x01, 0xDA }, 
			               	};
			CommandsQueue.Enqueue(c);
			c = new OBDCommand
			{
				StartTableOffset = (byte)ValuesOffset.Errors1,
				SendBytes = OBDHelper.GetSendBytesFor20H((byte)ValuesOffset.Errors1, 0x10)
			};
			CommandsQueue.Enqueue(c);
			c = new OBDCommand
			{
				StartTableOffset = (byte)ValuesOffset.Errors2,
				SendBytes = OBDHelper.GetSendBytesFor20H((byte)ValuesOffset.Errors2, 0x10)
			};
			CommandsQueue.Enqueue(c);
			c.LastCommandInCommandChain = true;
		}
		#endregion
	}
}