// This function generates and transmits UDS responses static void transmitResponse(TPUDSCANHandle Channel, byte serverAddr, ref TPUDSMsg UDSRequest) { TPUDSStatus Status; TPUDSMsg Message = new TPUDSMsg(); byte SI = UDSRequest.ServiceID; ushort lDataIdentifier; ushort lCount; ushort lMemorySizeLength; ushort lMemoryAddressLength; Random rnd = new Random(); // copy N_AI Message.NETADDRINFO = UDSRequest.NETADDRINFO; if (UDSRequest.NETADDRINFO.TA_TYPE == TPUDSAddressingType.PUDS_ADDRESSING_FUNCTIONAL) { // response to functional addressing is set to TEST_EQUIPMENT Message.NETADDRINFO.TA = (byte)TPUDSAddress.PUDS_ISO_15765_4_ADDR_TEST_EQUIPMENT; } else { Message.NETADDRINFO.TA = UDSRequest.NETADDRINFO.SA; } Message.NETADDRINFO.SA = serverAddr; Message.NETADDRINFO.TA_TYPE = TPUDSAddressingType.PUDS_ADDRESSING_PHYSICAL; // all responses are positive Message.DATA = new byte[UDSApi.PUDS_MAX_DATA]; Message.DATA[0] = (byte)(UDSRequest.ServiceID + UDSApi.PUDS_SI_POSITIVE_RESPONSE); // customize message response based on the UDS Service ID (see ISO 14229-1) switch ((TPUDSService)SI) { case TPUDSService.PUDS_SI_DiagnosticSessionControl: Message.DATA[1] = UDSRequest.DATA[1]; Message.DATA[2] = 0x00; // P2Can_Server_Max = 0x0010 Message.DATA[3] = 0x10; Message.DATA[4] = 0x03; // P2*Can_Server_Max = 0x03E8 Message.DATA[5] = 0xE8; Message.LEN = 6; break; case TPUDSService.PUDS_SI_ECUReset: Message.DATA[1] = UDSRequest.DATA[1]; Message.LEN = 2; if (UDSRequest.DATA[1] == (byte)UDSApi.TPUDSSvcParamER.PUDS_SVC_PARAM_ER_ERPSD) { Message.DATA[2] = 0x66; // power down time Message.LEN = 3; } break; case TPUDSService.PUDS_SI_SecurityAccess: Message.DATA[1] = UDSRequest.DATA[1]; Message.LEN = 2; if (UDSRequest.DATA[1] >= UDSApi.PUDS_SVC_PARAM_SA_RSD_MIN && UDSRequest.DATA[1] <= UDSApi.PUDS_SVC_PARAM_SA_RSD_MAX && UDSRequest.DATA[1] % 2 == 1) { // Request security seed are Even values // fill with dummy data Message.LEN = (ushort)((1 + SI) > UDSApi.PUDS_MAX_DATA ? UDSApi.PUDS_MAX_DATA : (1 + SI)); for (int i = 1; i < Message.LEN - 1; i++) // (LEN - 1) as POSITIVE.SI uses 1 byte { Message.DATA[i + 1] = (byte)(i + 1); } } break; case TPUDSService.PUDS_SI_CommunicationControl: Message.DATA[1] = UDSRequest.DATA[1]; Message.LEN = 2; break; case TPUDSService.PUDS_SI_TesterPresent: Message.DATA[1] = UDSRequest.DATA[1]; Message.LEN = 2; break; case TPUDSService.PUDS_SI_SecuredDataTransmission: // fill with dummy data (check Security-SubLayer record defined in ISO-15764) Message.LEN = (ushort)(1 + rnd.Next(0, 32767) % 50); for (int i = 0; i < Message.LEN - 1; i++) // (LEN - 1) as POSITIVE.SI uses 1 byte { Message.DATA[i + 1] = (byte)(i + 1); } break; case TPUDSService.PUDS_SI_ControlDTCSetting: Message.DATA[1] = UDSRequest.DATA[1]; Message.LEN = 2; break; case TPUDSService.PUDS_SI_ResponseOnEvent: Message.DATA[1] = UDSRequest.DATA[1]; if (UDSRequest.DATA[1] == (byte)UDSApi.TPUDSSvcParamROE.PUDS_SVC_PARAM_ROE_RAE) { Message.DATA[2] = 0; // # of activated events Message.LEN = 3; // EventType and ServiceToRespondTo Records not implemented } else { Message.DATA[2] = 0; // # of identified events Message.DATA[3] = UDSRequest.DATA[2]; // # event window time Message.LEN = 4; // EventType and ServiceToRespondTo Records not implemented } break; case TPUDSService.PUDS_SI_LinkControl: Message.DATA[1] = UDSRequest.DATA[1]; Message.LEN = 2; break; case TPUDSService.PUDS_SI_ReadDataByIdentifier: lCount = 0; for (int i = 0; i < UDSRequest.LEN - 1; i += 2) { // Use helper function to read network data (in big endian format) to WORD value (in windows i.e. little endian format) lDataIdentifier = Reverse16(UDSRequest.DATA[i + 1]); // copy DataIdentifier Message.DATA[lCount++ + 1] = UDSRequest.DATA[i + 1]; Message.DATA[lCount++ + 1] = UDSRequest.DATA[i + 2]; // DataRecord : fill with dummy data for (int j = 0; j < 5; j++) { Message.DATA[lCount++ + 1] = (byte)(j + 'A'); } } Message.LEN = (ushort)(lCount + 1); break; case TPUDSService.PUDS_SI_ReadMemoryByAddress: // read memorySize = bits [7..4] Message.LEN = (ushort)(1 + ((UDSRequest.DATA[2] >> 4) & 0xF)); // fill with dummy data for (int i = 0; i < Message.LEN - 1; i++) // (LEN - 1) as POSITIVE.SI uses 1 byte { Message.DATA[i + 1] = (byte)(i + 1); } break; case TPUDSService.PUDS_SI_ReadScalingDataByIdentifier: // Use helper function to read network data (in big endian format) to WORD value (in windows i.e. little endian format) lDataIdentifier = Reverse16(UDSRequest.DATA[1]); // copy DataIdentifier Message.DATA[1] = UDSRequest.DATA[1]; Message.DATA[2] = UDSRequest.DATA[2]; // create a formula Vehicule Speed = (0.75*x+30) km/h Message.DATA[3] = (0x0 << 4) | (0x1); // unSignedNumeric of 1 Bytes) Message.DATA[4] = 0x90; // formula, 0 data bytes Message.DATA[5] = 0x00; // formulaIdentifier = C0 * x + C1 Message.DATA[6] = 0xE0; // C0 high byte Message.DATA[7] = 0x4B; // C0 low byte Message.DATA[8] = 0x00; // C1 high byte Message.DATA[9] = 0x1E; // C1 low byte Message.DATA[10] = 0xA0; // unit/format, 0 data bytes Message.DATA[11] = 0x30; // unit ID, km/h Message.LEN = 11; break; case TPUDSService.PUDS_SI_ReadDataByPeriodicIdentifier: Message.LEN = 1; break; case TPUDSService.PUDS_SI_DynamicallyDefineDataIdentifier: Message.LEN = 4; Message.DATA[1] = UDSRequest.DATA[1]; // Use helper function to read network data (in big endian format) to WORD value (in windows i.e. little endian format) lDataIdentifier = Reverse16(UDSRequest.DATA[1]); // copy DataIdentifier Message.DATA[2] = UDSRequest.DATA[2]; Message.DATA[3] = UDSRequest.DATA[3]; break; case TPUDSService.PUDS_SI_WriteDataByIdentifier: Message.LEN = 3; // Use helper function to read network data (in big endian format) to WORD value (in windows i.e. little endian format) lDataIdentifier = Reverse16(UDSRequest.DATA[1]); // copy DataIdentifier Message.DATA[1] = UDSRequest.DATA[1]; Message.DATA[2] = UDSRequest.DATA[2]; break; case TPUDSService.PUDS_SI_WriteMemoryByAddress: // read MemorySizeLength & MemoryAddressLength lMemorySizeLength = (ushort)((UDSRequest.DATA[1] >> 4) & 0xF); lMemoryAddressLength = (ushort)(UDSRequest.DATA[1] & 0xF); Message.LEN = (ushort)(2 + lMemorySizeLength + lMemoryAddressLength); // copy Address and Memory parameters Message.DATA[1] = UDSRequest.DATA[1]; Array.Copy(UDSRequest.DATA, 2, Message.DATA, 2, lMemoryAddressLength); Array.Copy(UDSRequest.DATA, 2 + lMemoryAddressLength, Message.DATA, 2 + lMemoryAddressLength, lMemorySizeLength); break; case TPUDSService.PUDS_SI_ClearDiagnosticInformation: Message.LEN = 1; break; case TPUDSService.PUDS_SI_InputOutputControlByIdentifier: Message.LEN = 3; // Use helper function to read network data (in big endian format) to WORD value (in windows i.e. little endian format) lDataIdentifier = Reverse16(UDSRequest.DATA[1]); // copy DataIdentifier Message.DATA[1] = UDSRequest.DATA[1]; Message.DATA[2] = UDSRequest.DATA[2]; // ControlStatus Record not implemented break; case TPUDSService.PUDS_SI_RoutineControl: Message.LEN = 4; Message.DATA[1] = UDSRequest.DATA[1]; // Use helper function to read network data (in big endian format) to WORD value (in windows i.e. little endian format) lDataIdentifier = Reverse16(UDSRequest.DATA[1]); // copy DataIdentifier Message.DATA[2] = UDSRequest.DATA[2]; Message.DATA[3] = UDSRequest.DATA[3]; // RoutineStatus Record not implemented break; case TPUDSService.PUDS_SI_RequestDownload: case TPUDSService.PUDS_SI_RequestUpload: Message.LEN = (ushort)(2 + 1 + rnd.Next(0, 32767) % 50); Message.DATA[1] = 0xF0; // max number of block length = 0xF // fill with dummy data for (int i = 1; i < Message.LEN - 1; i++) // (LEN - 1) as POSITIVE.SI uses 1 byte { Message.DATA[i + 1] = (byte)(i + 1); } break; case TPUDSService.PUDS_SI_TransferData: Message.LEN = (ushort)((2 + UDSRequest.LEN) > UDSApi.PUDS_MAX_DATA ? UDSApi.PUDS_MAX_DATA : 2 + UDSRequest.LEN); // custom response to PCUClient example: // a. service is requested functionally, // b. 1st response is NRC ResponsePending // c. wait // d. send correct response if (UDSRequest.NETADDRINFO.TA_TYPE == TPUDSAddressingType.PUDS_ADDRESSING_FUNCTIONAL) { // Transmit a NRC ResponsePending response Message.LEN = 3; Message.DATA[0] = (byte)TPUDSService.PUDS_NR_SI; Message.DATA[1] = SI; Message.DATA[2] = UDSApi.PUDS_NRC_EXTENDED_TIMING; Status = UDSApi.Write(Channel, ref Message); Console.Write($"\n ...Transmitting a NRC Response Pending message: {Status}\n"); Console.Write($"\n ...simulating computation... (waiting ~{UDSApi.PUDS_P2CAN_ENHANCED_SERVER_MAX}ms)"); Thread.Sleep(UDSApi.PUDS_P2CAN_ENHANCED_SERVER_MAX - 100); // initialize real service response Message.LEN = UDSApi.PUDS_MAX_DATA; Message.DATA[0] = (byte)(UDSRequest.DATA[0] + UDSApi.PUDS_SI_POSITIVE_RESPONSE); } Message.DATA[1] = UDSRequest.DATA[1]; // fill with dummy data for (int i = 1; i < Message.LEN - 1; i++) // (LEN - 1) as POSITIVE.SI uses 1 byte { Message.DATA[i + 1] = (byte)(i + 1); } break; case TPUDSService.PUDS_SI_RequestTransferExit: Message.LEN = (ushort)(1 + 1 + rnd.Next(0, 32767) % 50); // fill with dummy data for (int i = 0; i < Message.LEN - 1; i++) // (LEN - 1) as POSITIVE.SI uses 1 byte { Message.DATA[i + 1] = (byte)(i + 1); } break; default: // fill with dummy data Message.LEN = (ushort)((1 + SI) > UDSApi.PUDS_MAX_DATA ? UDSApi.PUDS_MAX_DATA : (1 + SI)); Message.DATA[1] = SI; for (int i = 1; i < Message.LEN - 1; i++) // (LEN - 1) as POSITIVE.SI uses 1 byte { Message.DATA[i + 1] = (byte)(i + 1); } break; } // Transmit UDS response Status = UDSApi.Write(Channel, ref Message); Console.Write($"\n ...Transmitting response: {Status}\n"); UDSRequest = Message; }
public static void Main(string[] args) { TPUDSStatus Status; TPUDSCANHandle Channel; int nbErr = 0; uint param = 0x0; byte serverAddr = (byte)TPUDSAddress.PUDS_ISO_15765_4_ADDR_ECU_1; StringBuilder buff = new StringBuilder(); // Show version information UDSApi.GetValue(0, TPUDSParameter.PUDS_PARAM_API_VERSION, buff, 50); Console.Write($"PCAN-UDS API Version : {buff}\n"); // Sets the default PCAN-Channel to use (PCAN-USB Channel 2) // Channel = UDSApi.PUDS_USBBUS2; // Sets server address and channel from application arguments if specified if (args.Length == 3) { Channel = (TPCANHandle)(UDSApi.PUDS_USBBUS1 - 1 + TPCANHandle.Parse(args[1])); serverAddr = byte.Parse(args[2]); } // Initializing of the UDS Communication session // Status = UDSApi.Initialize(Channel, TPUDSBaudrate.PUDS_BAUD_250K, 0, 0, 0); Console.Write($"Initialize UDS: {Status} (chan. 0x{Channel:x2})\n"); // Define server address and filtered address // param = serverAddr; Status = UDSApi.SetValue(Channel, TPUDSParameter.PUDS_PARAM_SERVER_ADDRESS, ref param, (uint)Marshal.SizeOf(param)); Console.Write($"Set ServerAddress: {Status}\n"); Status = UDSApi.GetValue(Channel, TPUDSParameter.PUDS_PARAM_SERVER_ADDRESS, out param, (uint)Marshal.SizeOf(param)); Console.Write($"ServerAddress = 0x{param}\n"); // listen to the standard OBD functional address param = (uint)TPUDSAddress.PUDS_ISO_15765_4_ADDR_OBD_FUNCTIONAL | UDSApi.PUDS_SERVER_FILTER_LISTEN; Status = UDSApi.SetValue(Channel, TPUDSParameter.PUDS_PARAM_SERVER_FILTER, ref param, (uint)Marshal.SizeOf(param)); Console.Write($"Set Filtered Address: {Status}\n"); Console.Write("\nNote: press 'c' or 'C' to clear screen,"); Console.Write("\nNote: press 'q', 'Q' or '<Escape>' to quit...\n\n"); // Message Polling // TPUDSMsg Message; bool bStop = false; while (!bStop) { // check message Status = UDSApi.Read(Channel, out Message); if (Status == TPUDSStatus.PUDS_ERROR_OK) { // display message displayMessage(ref Message, Message.NETADDRINFO.SA != serverAddr); // check if an automatic reply should be sent if (Message.NETADDRINFO.SA != serverAddr && // do not reply to Tx Confirmation from this server Message.RESULT == TPUDSResult.PUDS_RESULT_N_OK) // and reply if there is no Network error { // do not reply if it was requested not to if (Message.NO_POSITIVE_RESPONSE_MSG == UDSApi.PUDS_SUPPR_POS_RSP_MSG_INDICATION_BIT) { Console.Write("\n ...Skipping response...\n"); } // do not reply to incoming message notification else if (Message.MSGTYPE != TPUDSMessageType.PUDS_MESSAGE_TYPE_INDICATION) { transmitResponse(Channel, serverAddr, ref Message); } } } Thread.Sleep(1); // check exit request if (Console.KeyAvailable) { switch (Console.ReadKey().KeyChar) { case 'q': case 'Q': case (char)27: //Escape bStop = true; break; case 'c': case 'C': Console.Clear(); Console.Write($"ServerAddress = 0x{serverAddr:x2}\n"); Console.Write("\nNote: press 'c' or 'C' to clear screen,"); Console.Write("\nNote: press 'q', 'Q' or '<Escape>' to quit...\n\n"); break; } } } // Display a small report if (nbErr > 0) { Console.Write($"\nERROR : {nbErr} errors occured.\n\n"); Console.Write("\n\nPress <Enter> to quit..."); Console.ReadKey(true); } // Release channel UDSApi.Uninitialize(Channel); }