// debug stub, not intended for direct execution private void SetSessionStub(ECUConnection Connection) { return; Console.WriteLine("Entering bootmode"); Connection.SendMessage(BitUtility.BytesFromHex("1002")); Console.WriteLine("Requesting for seed"); byte[] seed = Connection.SendMessage(BitUtility.BytesFromHex("2705")); SecurityAccess.SecurityAutoLogin.QueryUnlockEcu(seed.Skip(2).ToArray(), "X_4", 5, out byte[] key); Console.Write("Authenticating for bootmode.. "); List <byte> keyResponse = new List <byte>(new byte[] { 0x27, 0x06 }); keyResponse.AddRange(key); byte[] authResponse = Connection.SendMessage(keyResponse); Console.WriteLine(BitUtility.BytesToHex(authResponse)); // remind myself to write fingerprint Console.WriteLine($"2EF15A 00 0004 150213 00000000\r\n2EF15A 01 0004 150213 00000000\r\n3101FF00"); /* * int blockId = 1; // block0: CODE, block1: DATA * Console.WriteLine($"Writing fingerprint"); * // 2e f1 5a 00 00 04 15 02 16 00 00 00 00 * Connection.SendMessage(BitUtility.BytesFromHex($"2EF15A {blockId:X2} 0004 150213 00000000")); * * // 2EF15A 00 0004 150213 00000000 * // 2EF15A 01 0004 150213 00000000 * * Console.WriteLine($"Erasing block {blockId}"); * Connection.SendMessage(BitUtility.BytesFromHex("3101FF00")); * Console.WriteLine("Ready to accept flash data"); */ }
// FIN = Fahrzeug-Identifizierungs-Nummer // VIN = Vehicle Identification Number public static bool TryReadChassisNumber(ECUConnection connection, out string chassisNumber) { chassisNumber = ""; // we need a existing hardware connection to a J2534 device if (connection is null) { return(false); } if (connection.IsSimulation()) { return(false); } if (connection.ConnectionDevice is null) { // no valid j2534 device initialized yet, can't do anything return(false); } bool connectionWasEstablished = connection.ConnectionProtocol != null; // if the connection is already open, we can use the protocol's vin query int[] baudratesToTest = new int[] { 500000, 800000 }; foreach (int baudrateToTest in baudratesToTest) { // if there is an established connection (i.e. already connected and identified a target, do not reconfigure the connection if (!connectionWasEstablished) { SetupConnectionForBaud(connection, baudrateToTest); } // try KW2C3PE first, since it is a legacy protocol; UDS seems to be somewhat aware, and deliberately avoids known KW2C3PE identifiers if (TryReadKW2C3PEChassisNumber(connection, out string KW2C3PEChassisNumber)) { chassisNumber = KW2C3PEChassisNumber; return(true); } if (TryReadUDSChassisNumber(connection, out string udsChassisNumber)) { chassisNumber = udsChassisNumber; return(true); } // if there's an established connection that failed both UDS/KW2C3PE, the loop is no longer required if (connection.ConnectionProtocol != null) { return(false); } } // if a connection was unavailable, it would have been established earlier on; clean it up if (!connectionWasEstablished) { connection.TryCleanup(); connection = null; } return(false); }
public string GetHtmlTable(ECUConnection connection) { ECUMetadata metadata = connection.ConnectionProtocol.QueryECUMetadata(connection); string[][] rows = new string[][] { new string[] { "Variant ID", metadata.VariantID.ToString("X4") }, new string[] { "Gateway Mode", metadata.GatewayMode.ToString() }, new string[] { "Boot Version", metadata.BootVersion }, new string[] { "Serial Number", metadata.SerialNumber }, new string[] { "Chassis Number (Current)", metadata.ChassisNumberCurrent }, new string[] { "Chassis Number (Original)", metadata.ChassisNumberOriginal }, new string[] { "Hardware Part Number", metadata.HardwarePartNumber }, new string[] { "Hardware Version", metadata.HardwareVersion }, new string[] { "Hardware Supplier", ECUMetadata.GetVendorName(metadata.VendorID) }, }; StringBuilder ecuTableRows = new StringBuilder(); foreach (string[] row in rows) { ecuTableRows.AppendLine($"<tr><td>{row[0]}</td><td>{row[1]}</td></tr>"); } StringBuilder swBlockRows = new StringBuilder(); foreach (ECUFlashMetadata flash in metadata.FlashMetadata) { string flashStatus = flash.StatusID == 1 ? "Valid" : "Invalid"; swBlockRows.AppendLine($@" <h3>SW Block #{flash.Index}</h3> <table> <tr><td>Part Number</td><td>{flash.PartNumber}</td></tr> <tr><td>Version</td><td>{flash.Version}</td></tr> <tr><td>Vendor</td><td>{ECUMetadata.GetVendorName(flash.VendorID)}</td></tr> <tr><td>Status</td><td>{flashStatus}</td></tr> <tr><td>Last Flash Vendor</td><td>{ECUMetadata.GetVendorName(flash.LastFlashVendor)}</td></tr> <tr><td>Last Flash Date</td><td>{flash.FlashDate}</td></tr> <tr><td>Flash Fingerprint</td><td>{flash.FlashFingerprint}</td></tr> </table> "); } return($@" <hr> <h3>Hardware</h3> <table> {ecuTableRows} </table> {swBlockRows} "); }
private void J2534InterfaceItem_Click(object sender, EventArgs e) { ToolStripItem caller = (ToolStripItem)sender; if (Connection != null) { Connection.TryCleanup(); } Connection = new ECUConnection(caller.Tag.ToString(), caller.Text); Connection.ConnectionStateChangeEvent += ConnectionStateChangedHandler; Connection.OpenDevice(); // loadtree should not be necessary if the prior state was disconnected // LoadTree(); }
private void SetDisconnectedState(bool refresh = true) { // disconnected really means "running in simulation mode" if (Connection != null) { Connection.TryCleanup(); } Connection = new ECUConnection(); Connection.ConnectionStateChangeEvent += ConnectionStateChangedHandler; if (refresh) { LoadTree(); } }
public static bool TryReadKW2C3PEChassisNumber(ECUConnection connection, out string chassisNumber) { chassisNumber = ""; byte[] response = connection.SendMessage(new byte[] { 0x1A, 0x90 }); // VIN current if (response.Length != 19) // 5A 90 + 17byte vin { return(false); } if (response[0] != 0x5A) { return(false); } chassisNumber = Encoding.ASCII.GetString(response.Skip(2).ToArray()); return(true); }
public static bool TryReadUDSChassisNumber(ECUConnection connection, out string chassisNumber) { chassisNumber = ""; byte[] response = connection.SendMessage(new byte[] { 0x22, 0xF1, 0xA0 }); if (response.Length != 20) // 62 F1 A0 + 17byte vin { return(false); } if (response[0] != 0x62) { return(false); } chassisNumber = Encoding.ASCII.GetString(response.Skip(3).ToArray()); return(true); }
private void j2534InterfacesToolStripMenuItem_DropDownOpening(object sender, EventArgs e) { j2534InterfacesToolStripMenuItem.DropDownItems.Clear(); ToolStripItem defaultItem = j2534InterfacesToolStripMenuItem.DropDownItems.Add("(No devices found)"); defaultItem.Enabled = false; foreach (Tuple <string, string> device in ECUConnection.GetAvailableJ2534NamesAndDrivers()) { defaultItem.Visible = false; ToolStripItem newItem = j2534InterfacesToolStripMenuItem.DropDownItems.Add(device.Item1); newItem.Tag = device.Item2; newItem.Click += J2534InterfaceItem_Click; } }
private static void ExecVCWrite(byte[] request, DiagService service, ECUConnection connection, bool writesEnabled) { bool allowVcWrite = writesEnabled; // allowWriteVariantCodingToolStripMenuItem.Checked; if (allowVcWrite) { connection.ExecUserDiagJob(request, service); Console.WriteLine("VC Write completed"); } else { MessageBox.Show("This VC write action has to be manually enabled under \r\nFile > Allow Write Variant Coding\r\nPlease make sure that you understand the risks before doing so.", "Accidental Brick Protection"); } }
public static void ShowMetadataModal(ECUConnection connection) { if (connection.ConnectionProtocol is null) { Console.WriteLine("Please initiate contact with a target first."); return; } ECUMetadata metadata = connection.ConnectionProtocol.QueryECUMetadata(connection); string[][] rows = new string[][] { new string[] { "Variant ID", metadata.VariantID.ToString("X4") }, new string[] { "Gateway Mode", metadata.GatewayMode.ToString() }, new string[] { "Boot Version", metadata.BootVersion }, new string[] { "Serial Number", metadata.SerialNumber }, new string[] { "Chassis Number (Current)", metadata.ChassisNumberCurrent }, new string[] { "Chassis Number (Original)", metadata.ChassisNumberOriginal }, new string[] { "Hardware Part Number", metadata.HardwarePartNumber }, new string[] { "Hardware Version", metadata.HardwareVersion }, new string[] { "Hardware Supplier", ECUMetadata.GetVendorName(metadata.VendorID) }, }; List <string[]> rowsAsList = new List <string[]>(rows); foreach (ECUFlashMetadata flash in metadata.FlashMetadata) { string blockPrefix = $"SW Block #{flash.Index} : "; string flashStatus = flash.StatusID == 1 ? "Valid" : "Invalid"; rowsAsList.Add(new string[] { $"{blockPrefix}Descriptor", $"{flash.PartNumber} [Version {flash.Version} from {ECUMetadata.GetVendorName(flash.VendorID)}]" }); rowsAsList.Add(new string[] { $"{blockPrefix}Status", $"{flashStatus}, Last flashed by {ECUMetadata.GetVendorName(flash.LastFlashVendor)} on {flash.FlashDate} (Fingerprint: {flash.FlashFingerprint})" }); } GenericPicker picker = new GenericPicker(rowsAsList.ToArray(), new string[] { "Attribute", "Value" }); picker.Text = "ECU Metadata"; picker.ShowDialog(); }
public UDSHexEditor(ECUConnection connection) { InitializeComponent(); Connection = connection; // missing docs for HexBox : https://stackoverflow.com/questions/53410380/how-to-create-hex-editor-inside-of-winforms-app Hexbox = new HexBox() { /* * UseFixedBytesPerLine = true, * BytesPerLine = 16, */ ColumnInfoVisible = true, LineInfoVisible = true, StringViewVisible = true, VScrollBarVisible = true }; Hexbox.Dock = DockStyle.Fill; splitContainer1.Panel2.Controls.Add(Hexbox); Hexbox.LineInfoOffset = 0; Hexbox.ByteProvider = new DynamicByteProvider(new byte[] { }); }
// eventually move this to ecuconnection, since it touches J2534Sharp and this should be kept local public static void SetupConnectionForBaud(ECUConnection connection, int baud) { if (connection.ConnectionChannel != null) { connection.ConnectionChannel.ClearMsgFilters(); // this bit here for good luck since there are some misbehaving j2534 devices connection.ConnectionChannel.Dispose(); connection.ConnectionChannel = null; } try { // create a new channel; the protocol is almost always ISO15765 connection.ConnectionChannel = connection.ConnectionDevice.GetChannel(Protocol.ISO15765, (Baud)baud, ConnectFlag.CAN_ID_BOTH); connection.ConnectionProtocol = new DiagnosticProtocol.UnsupportedProtocol(); connection.ConnectionChannel.DefaultTxFlag = TxFlag.ISO15765_FRAME_PAD; connection.InternalTimeout = 100; // setup filters connection.SetCANIdentifiers(0x7E0, 0x7E8); connection.J2534SetFilters(); // setup config with some sane defaults since we are guessing our target List <SConfig> sconfigList = new List <SConfig>(); sconfigList.Add(new SConfig(Parameter.STMIN_TX, 0)); sconfigList.Add(new SConfig(Parameter.ISO15765_STMIN, 0)); sconfigList.Add(new SConfig(Parameter.ISO15765_BS, 8)); connection.ConnectionChannel.SetConfig(sconfigList.ToArray()); // flush the buffer connection.ConnectionChannel.ClearRxBuffer(); connection.ConnectionChannel.ClearTxBuffer(); } catch (Exception e) { Console.WriteLine($"[!] ECUIdentification exception in SetupConnectionForBaud: {e.Message}"); } }
public VCForm(CaesarContainer container, string ecuName, string variantName, string vcDomain, ECUConnection connection) { InitializeComponent(); ECUName = ecuName; VariantName = variantName; VCDomainName = vcDomain; SelectedECU = container.GetECUByName(ecuName); ECUVariant = container.GetECUVariantByName(variantName); VariantCodingDomain = ECUVariant.GetVCDomainByName(VCDomainName); ReadService = ECUVariant.GetDiagServiceByName(VariantCodingDomain.ReadServiceName); WriteService = ECUVariant.GetDiagServiceByName(VariantCodingDomain.WriteServiceName); if ((ReadService is null) || (WriteService is null)) { Console.WriteLine("VC Dialog: Unable to proceed - could not find referenced diagnostic services"); this.Close(); } //Console.WriteLine(ReadService.Qualifier); //Console.WriteLine(WriteService.Qualifier); VCValue = new byte[VariantCodingDomain.DumpSize]; UnfilteredReadValue = new byte[] { }; foreach (Tuple <string, byte[]> row in VariantCodingDomain.DefaultData) { if (row.Item1.ToLower() == "default" && (row.Item2.Length == VariantCodingDomain.DumpSize)) { VCValue = row.Item2; Console.WriteLine("Default CBF variant coding data is available"); break; } } if (connection.State >= ECUConnection.ConnectionState.ChannelConnectedPendingEcuContact) { // Console.WriteLine($"Requesting variant coding read: {ReadService.Qualifier} : ({BitUtility.BytesToHex(ReadService.RequestBytes)})"); byte[] response = connection.SendDiagRequest(ReadService); DiagPreparation largestPrep = GetLargestPreparation(ReadService.OutputPreparations); if (largestPrep.PresPoolIndex > -1) { // DiagPresentation pres = SelectedECU.GlobalPresentations[largestPrep.PresPoolIndex]; // pres.PrintDebug(); } // Console.WriteLine($"Variant coding received: {BitUtility.BytesToHex(response)}"); VCValue = response.Skip(largestPrep.BitPosition / 8).Take(largestPrep.SizeInBits / 8).ToArray(); // store the received VC: when writing back, we might need the previous author's fingerprints UnfilteredReadValue = response; } else { Console.WriteLine("Please check for connectivity to the target ECU (could not read variant coding data)"); MessageBox.Show("Variant Coding dialog will operate as a simulation using default values.", "Unable to read ECU variant coding data", MessageBoxButtons.OK); btnApply.Enabled = false; } // VCSanityCheck(); IntepretVC(); PresentVC(); }
public static void CreateDTCReport(List <DTCContext> dtcContexts, ECUConnection connection, ECUVariant variant) { Cursor.Current = Cursors.WaitCursor; string reportDate = $"{DateTime.Now.ToShortDateString()} {DateTime.Now.ToLongTimeString()}"; string containerChecksum = variant.ParentECU.ParentContainer.FileChecksum.ToString("X8"); string dVersion = MainForm.GetVersion(); string cVersion = CaesarContainer.GetCaesarVersionString(); string connectionData = connection is null ? "(Unavailable)" : connection.FriendlyProfileName; string ecuCbfVersion = variant.ParentECU.EcuXmlVersion; StringBuilder tableBuilder = new StringBuilder(); // back up every domain since some have overlaps foreach (DTCContext dtcCtx in dtcContexts) { StringBuilder tableRowBuilder = new StringBuilder(); // env, description for (int i = 0; i < dtcCtx.EnvironmentContext.Count; i++) { string tableRowBlock = $@" <tr> <td>{dtcCtx.EnvironmentContext[i][0]}</td> <td>{dtcCtx.EnvironmentContext[i][1]}</td> </tr> "; tableRowBuilder.Append(tableRowBlock); } string tableBlock = $@" <hr> <h2>{dtcCtx.DTC.Qualifier}</h2> <p>{dtcCtx.DTC.Description}</p> <table> <tr> <th>Environment</th> <th>Description</th> </tr> {tableRowBuilder} </table> "; tableBuilder.Append(tableBlock); } string document = $@" <!DOCTYPE html> <html lang=""en""> <head> <meta charset=""UTF-8""> <title>{variant.ParentECU.Qualifier} : DTC Report</title> <style> body {{ padding: 10px 20% 15px 15%; font-family: sans-serif; }} .pull-right {{ float: right; }} hr {{ border-bottom: 0; opacity: 0.2; }} table {{ width: 100%; margin: 20px 0; }} #eof {{ text-transform: uppercase; font-weight: bold; opacity: 0.15; letter-spacing: 0.4em; }} .coding-data {{ opacity: 0.8; }} .monospace {{ font-family: monospace; }} .fifth {{ width: 20%; }} th {{ text-align: left; }} </style> </head> <body> <h1 class=""pull-right"">Diogenes</h1> <h1>{variant.ParentECU.Qualifier}</h1> <hr> <table> <tr> <td>CBF Checksum</td> <td>{containerChecksum}</td> </tr> <tr> <td>Date</td> <td>{reportDate}</td> </tr> <tr> <td>Client Version</td> <td>Diogenes: {dVersion}, Caesar: {cVersion}</td> </tr> <tr> <td>ECU CBF Version</td> <td>{ecuCbfVersion}</td> </tr> <tr> <td>ECU Variant</td> <td>{variant.Qualifier}</td> </tr> <tr> <td>Connection Info</td> <td>{connectionData}</td> </tr> </table> {connection.ConnectionProtocol.QueryECUMetadata(connection).GetHtmlTable(connection)} {tableBuilder} <hr> <span id=""eof"">End of report</span> </body> </html>"; Cursor.Current = Cursors.Default; SaveFileDialog sfd = new SaveFileDialog(); sfd.Title = "Specify a location to save your new DTC report"; sfd.Filter = "HTML file (*.html)|*.html|All files (*.*)|*.*"; sfd.FileName = $"DTC_{variant.Qualifier}_{DateTime.Now.ToString("yyyyMMdd_HHmm")}.html"; if (sfd.ShowDialog() == DialogResult.OK) { File.WriteAllText(sfd.FileName, document.ToString()); MessageBox.Show($"Report successfully saved to {sfd.FileName}", "Export complete"); } }
public DTCForm(ECUConnection connection, ECUVariant variant) { Connection = connection; Variant = variant; InitializeComponent(); }
public static void treeViewSelectVariantCodingBackup(TreeNode node, ECUConnection connection, List <CaesarContainer> containers) { if (connection is null) { return; } Cursor.Current = Cursors.WaitCursor; string variantName = node.Parent.Text; string ecuName = node.Parent.Parent.Text; string reportDate = $"{DateTime.Now.ToShortDateString()} {DateTime.Now.ToLongTimeString()}"; StringBuilder report = new StringBuilder(); CaesarContainer container = containers.Find(x => x.GetECUVariantByName(variantName) != null); ECU ecu = container.GetECUByName(ecuName); ECUVariant variant = container.GetECUVariantByName(variantName); string containerChecksum = container.FileChecksum.ToString("X8"); string dVersion = MainForm.GetVersion(); string cVersion = CaesarContainer.GetCaesarVersionString(); string connectionData = connection is null ? "(Unavailable)" : connection.FriendlyProfileName; string ecuCbfVersion = ecu.EcuXmlVersion; report.Append($"ECU Variant: {variant.Qualifier}\r\n"); StringBuilder tableBuilder = new StringBuilder(); // back up every domain since some have overlaps foreach (VCDomain domain in variant.VCDomains) { report.Append($"\r\nCoding Service: {domain.Qualifier}\r\n"); // find the read service, then execute it as-is DiagService readService = variant.GetDiagServiceByName(domain.ReadServiceName); byte[] response = connection.SendDiagRequest(readService); // isolate the traditional vc string DiagPreparation largestPrep = VCForm.GetLargestPreparation(readService.OutputPreparations); byte[] vcValue = response.Skip(largestPrep.BitPosition / 8).Take(largestPrep.SizeInBits / 8).ToArray(); StringBuilder tableRowBuilder = new StringBuilder(); // explain the vc string's settings for (int i = 0; i < domain.VCFragments.Count; i++) { VCFragment currentFragment = domain.VCFragments[i]; VCSubfragment subfragment = currentFragment.GetSubfragmentConfiguration(vcValue); string fragmentValue = subfragment is null ? "(?)" : subfragment.NameCTFResolved; string fragmentSupplementKey = subfragment is null ? "(?)" : subfragment.SupplementKey; string tableRowBlock = $@" <tr> <td>{currentFragment.Qualifier}</td> <td>{fragmentValue}</td> <td>{fragmentSupplementKey}</td> </tr> "; tableRowBuilder.Append(tableRowBlock); } string tableBlock = $@" <hr> <h2>{domain.Qualifier}</h2> <table class=""coding-data""> <tr> <td class=""fifth"">Coding String (Hex)</td> <td class=""monospace"">{BitUtility.BytesToHex(vcValue, true)}</td> </tr> <tr> <td class=""fifth"">Raw Coding String (Hex)</td> <td class=""monospace"">{BitUtility.BytesToHex(response, true)}</td> </tr> </table> <table> <tr> <th>Fragment</th> <th>Value</th> <th>Supplement Key</th> </tr> {tableRowBuilder} </table> "; tableBuilder.Append(tableBlock); } string document = $@" <!DOCTYPE html> <html lang=""en""> <head> <meta charset=""UTF-8""> <title>{ecuName} : Backup</title> <style> body {{ padding: 10px 20% 15px 15%; font-family: sans-serif; }} .pull-right {{ float: right; }} hr {{ border-bottom: 0; opacity: 0.2; }} table {{ width: 100%; margin: 20px 0; }} #eof {{ text-transform: uppercase; font-weight: bold; opacity: 0.15; letter-spacing: 0.4em; }} .coding-data {{ opacity: 0.8; }} .monospace {{ font-family: monospace; }} .fifth {{ width: 20%; }} th {{ text-align: left; }} </style> </head> <body> <h1 class=""pull-right"">Diogenes</h1> <h1>{ecuName}</h1> <hr> <table> <tr> <td>CBF Checksum</td> <td>{containerChecksum}</td> </tr> <tr> <td>Date</td> <td>{reportDate}</td> </tr> <tr> <td>Client Version</td> <td>Diogenes: {dVersion}, Caesar: {cVersion}</td> </tr> <tr> <td>ECU CBF Version</td> <td>{ecuCbfVersion}</td> </tr> <tr> <td>ECU Variant</td> <td>{variantName}</td> </tr> <tr> <td>Connection Info</td> <td>{connectionData}</td> </tr> </table> {tableBuilder} <hr> <span id=""eof"">End of report</span> </body> </html>"; Cursor.Current = Cursors.Default; SaveFileDialog sfd = new SaveFileDialog(); sfd.Title = "Specify a location to save your new VC backup"; sfd.Filter = "HTML file (*.html)|*.html|All files (*.*)|*.*"; sfd.FileName = $"VC_{variantName}_{DateTime.Now.ToString("yyyyMMdd_HHmm")}.html"; if (sfd.ShowDialog() == DialogResult.OK) { File.WriteAllText(sfd.FileName, document.ToString()); MessageBox.Show($"Backup successfully saved to {sfd.FileName}", "Export complete"); } }
/* * test: med40 * * jg: dumping pres * jg: q: SID_RQ pos byte: 0 size bytes: 1 modecfg:323 fieldtype: IntegerType dump: 2E 00 00 00 * jg: q: RecordDataIdentifier pos byte: 1 size bytes: 2 modecfg:324 fieldtype: IntegerType dump: 01 10 00 00 * jg: q: #0 pos byte: 33 size bytes: 16 modecfg:6430 fieldtype: BitDumpType dump: * jg: q: #1 pos byte: 49 size bytes: 1 modecfg:6423 fieldtype: IntegerType dump: * jg: q: #2 pos byte: 50 size bytes: 1 modecfg:6423 fieldtype: IntegerType dump: * jg: q: #3 pos byte: 51 size bytes: 1 modecfg:6423 fieldtype: IntegerType dump: * jg: q: #4 pos byte: 52 size bytes: 1 modecfg:6423 fieldtype: IntegerType dump: * jg: q: #5 pos byte: 3 size bytes: 50 modecfg:6410 fieldtype: ExtendedBitDumpType dump: * jg: done dumping pres * */ public static void DoVariantCoding(ECUConnection connection, VCForm vcForm, bool writesEnabled) { Console.WriteLine($"Operator requesting for VC: {BitUtility.BytesToHex(vcForm.VCValue, true)}"); RunDiagForm runDiagForm = new RunDiagForm(vcForm.WriteService); // construct a write command from presentations: fill up the VC value first, fill up all available dumps, then inherit the last values (fingerprints, scn) from the read command byte[] vcParameter = vcForm.VCValue; byte[] writeCommand = vcForm.WriteService.RequestBytes; byte[] priorReadCommand = vcForm.UnfilteredReadValue; // start with a list of all values that we will have to fill List <DiagPreparation> preparationsToProcess = new List <DiagPreparation>(vcForm.WriteService.InputPreparations); // fill up vc, which pretty much always uses the ExtendedBitDumpType type DiagPreparation vcPrep = preparationsToProcess.Find(x => x.FieldType == DiagPreparation.InferredDataType.ExtendedBitDumpType); if (vcPrep is null) { MessageBox.Show("VC: Could not find the VC ExtendedBitDump prep, stopping early to save your ECU.", "VC Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } int vcPrepSizeInBytes = vcPrep.SizeInBits / 8; int vcPrepBytePosition = vcPrep.BitPosition / 8; if (vcPrepSizeInBytes < vcParameter.Length) { MessageBox.Show("VC: VC string is longer than the parameter can fit, stopping early to save your ECU.", "VC Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } // zero out the destination buffer for the param that we intend to write since there's a possibility that our param is shorter than the actual prep's size for (int i = vcPrepBytePosition; i < (vcPrepBytePosition + vcPrepSizeInBytes); i++) { writeCommand[i] = 0; } // copy the parameter in Array.ConstrainedCopy(vcParameter, 0, writeCommand, vcPrepBytePosition, vcParameter.Length); preparationsToProcess.Remove(vcPrep); // done with vc prep // merge prefilled constants such as the (SID_RQ, id..) List <DiagPreparation> prefilledValues = new List <DiagPreparation>(); foreach (DiagPreparation prep in preparationsToProcess) { if (prep.Dump.Length > 0) { prefilledValues.Add(prep); if (prep.FieldType == DiagPreparation.InferredDataType.IntegerType) { byte[] fixedDump = prep.Dump.Take(prep.SizeInBits / 8).Reverse().ToArray(); Array.ConstrainedCopy(vcParameter, 0, writeCommand, vcPrepBytePosition, vcParameter.Length); } else { Console.WriteLine($"Skipping prefill for {prep.Qualifier} as the data type {prep.FieldType} is unsupported."); } } } // "mark" the constants as done foreach (DiagPreparation prep in prefilledValues) { preparationsToProcess.Remove(prep); } // isolate the SCN if it exists foreach (DiagPreparation prep in preparationsToProcess) { int bytePosition = prep.BitPosition / 8; int byteLength = prep.SizeInBits / 8; // SCN is always 16-bytes if (byteLength != 16) { continue; } byte[] originalValue = new byte[byteLength]; Array.ConstrainedCopy(priorReadCommand, bytePosition, originalValue, 0, byteLength); // SCN values are ASCII numerals (between 0x30 - 0x39) bool isValidSCN = true; foreach (byte b in originalValue) { if ((b > 0x39) || (b < 0x30)) { isValidSCN = false; break; } } if (!isValidSCN) { continue; } Console.WriteLine($"Found SCN value: {Encoding.ASCII.GetString(originalValue)}"); // check if operator is using vediamo-style variant-coding, where the SCN is set to 0000000000000000 when writing VC // otherwise, we can reuse the last value as read from the ECU bool useVediamoBehaviorZeroSCN = Preferences.GetValue(Preferences.PreferenceKey.EnableSCNZero) == "true"; if (useVediamoBehaviorZeroSCN) { for (int i = 0; i < originalValue.Length; i++) { originalValue[i] = 0x30; } } Console.WriteLine($"Using {Encoding.ASCII.GetString(originalValue)} as new SCN value"); // write-back the recognized (and optionally, modified) SCN Array.ConstrainedCopy(originalValue, 0, writeCommand, bytePosition, byteLength); preparationsToProcess.Remove(prep); break; } // isolate the fingerprint if it exists // normally fingerprint is appended at the tail of the VC command List <int> tailIndices = new List <int>() { writeCommand.Length - 1, writeCommand.Length - 2, writeCommand.Length - 3, writeCommand.Length - 4 }; List <DiagPreparation> possibleFingerprintFields = new List <DiagPreparation>(); foreach (DiagPreparation prep in preparationsToProcess) { int bytePosition = prep.BitPosition / 8; int byteLength = prep.SizeInBits / 8; if ((prep.FieldType == DiagPreparation.InferredDataType.IntegerType) && (byteLength == 1) && (tailIndices.Contains(bytePosition))) { tailIndices.Remove(bytePosition); possibleFingerprintFields.Add(prep); } } if (possibleFingerprintFields.Count == 4) { // copy in the fingerprint, and "mark" the constants as done byte[] fingerprint = priorReadCommand.Skip(priorReadCommand.Length - 4).ToArray(); Console.WriteLine($"Found original fingerprint as {BitUtility.BytesToHex(fingerprint)}"); // default behavior is to clone last fingerprint, else use stored fingerprint if (Preferences.GetValue(Preferences.PreferenceKey.EnableFingerprintClone) == "false") { uint altFingerprintValue = uint.Parse(Preferences.GetValue(Preferences.PreferenceKey.FingerprintValue)); fingerprint[3] = (byte)(altFingerprintValue & 0xFF); altFingerprintValue >>= 8; fingerprint[2] = (byte)(altFingerprintValue & 0xFF); altFingerprintValue >>= 8; fingerprint[1] = (byte)(altFingerprintValue & 0xFF); altFingerprintValue >>= 8; fingerprint[0] = (byte)(altFingerprintValue & 0xFF); } Console.WriteLine($"Using {BitUtility.BytesToHex(fingerprint)} as new fingerprint value."); Array.ConstrainedCopy(fingerprint, 0, writeCommand, writeCommand.Length - 4, 4); foreach (DiagPreparation prep in possibleFingerprintFields) { preparationsToProcess.Remove(prep); } } // at this point, whatever that's left in preparationsToProcess are stuff that are variable, but should be copied verbatim from the original read request (e.g. fingerprints, scn) // log the assumptions, show it the operator just in case StringBuilder assumptionsMade = new StringBuilder(); if (preparationsToProcess.Count > 0) { if (writeCommand.Length != priorReadCommand.Length) { MessageBox.Show("There are some preparations that do not have a default value. \r\n" + "The input and output values do not have matching lengths, which means that the automatic assumption may be wrong. \r\n" + "Please be very careful when proceeding.", "Warning"); } foreach (DiagPreparation prep in preparationsToProcess) { int bytePosition = prep.BitPosition / 8; int byteLength = prep.SizeInBits / 8; Array.ConstrainedCopy(priorReadCommand, bytePosition, writeCommand, bytePosition, byteLength); assumptionsMade.Append($"{prep.Qualifier} : {BitUtility.BytesToHex(priorReadCommand.Skip(bytePosition).Take(byteLength).ToArray(), true)}\r\n"); } } /* * // lazy me dumping the values * for (int i = 0; i < vcForm.WriteService.InputPreparations.Count; i++) * { * DiagPreparation prep = vcForm.WriteService.InputPreparations[i]; * Console.WriteLine($"debug: q: {prep.Qualifier} pos byte: {(prep.BitPosition / 8)} size bytes: {(prep.SizeInBits / 8)} modecfg:{prep.ModeConfig:X} fieldtype: {prep.FieldType} dump: {BitUtility.BytesToHex(prep.Dump, true)}"); * } */ // we are done preparing the command, if we are confident we can send the command straight to the ECU, else, let the user review if (assumptionsMade.Length > 0) { if (MessageBox.Show("Some assumptions were made when preparing the write parameters. \r\n\r\n" + "You may wish to review them by selecting Cancel, or select OK to execute the write command immediately.\r\n\r\n" + assumptionsMade.ToString(), "Review assumptions", MessageBoxButtons.OKCancel, MessageBoxIcon.Information) == DialogResult.OK) { ExecVCWrite(writeCommand, vcForm.WriteService, connection, writesEnabled); } else { runDiagForm.Result = writeCommand; if (runDiagForm.ShowDialog() == DialogResult.OK) { ExecVCWrite(runDiagForm.Result, vcForm.WriteService, connection, writesEnabled); } } } else { // everything accounted for, immediately write ExecVCWrite(writeCommand, vcForm.WriteService, connection, writesEnabled); } }
public BlockDownload(ECUConnection connection) { this.connection = connection; InitializeComponent(); }