GM Gen 4 data logger
Posted: Sun Aug 25, 2024 7:47 pm
Half way through making a read by address data logger for e38, 67 and t43. Will most likely work on gen 5 stuff as well. A very kind member on this forum has been helping me out with some testing, he's on vacation the lucky bugger so I'm working the code part so he has something substantial to test with when he's back at his shop. We've got service service $2D and $22 working on a T43. I'm wanting to take it to the next level and get service $AA working. First off can I clarify the message creation flow path.
And lastly when creating the $2C objects, is there a limit of pids per dpid?

And extra lastly, I'm trying to obey the spec with regards to the pci frames and negative response handling. Just wondering if those skilled in the art wouldn't mind taking a look? I'm a hack when it comes to code.
Code: Select all
* $2D --> OneShot --> $22
* |
* -> $2C --> Endless --> $AA

And extra lastly, I'm trying to obey the spec with regards to the pci frames and negative response handling. Just wondering if those skilled in the art wouldn't mind taking a look? I'm a hack when it comes to code.
Code: Select all
public static void SendCommand(PID.Pid_Obj pid_Obj, List<byte[]> sendCommand, List<byte[]> simReturnMessage = null)
{
int retryCnt_FlowCtrl = 0;
int negativeResponse = 0;
int dbg_RxMsgIndx = 0;
// Perform initialisation ops
pid_Obj.Payload.Clear();
pid_Obj.Status = PID.J2534_Message.status.OK;
pid_Obj.ErrorMsg = PID.J2534_Message.errorReason.None;
GetMessageResults messages = null;
int timeOut = pid_Obj.Timeout;
try
{
// Number of messages to send
for (int TxMsgIndx = 0; TxMsgIndx < sendCommand.Count; TxMsgIndx++)
{
// Send out the message to the node
if (!Form1.SIM_MODE)
{
Channel.SendMessage(sendCommand[TxMsgIndx]);
}
// Save the full length send command to the pid object for our reference
pid_Obj.TxBytes = sendCommand[TxMsgIndx];
// Logs the tx bytes to the status console
if (Logger.LogTxRxMsgsToStatusConsole) Logger.WriteLineAsync($"{Form1.indent} Tx {BitConverter.ToString(pid_Obj.TxBytes).Replace("-", " ").Remove(0, 12)}");
// Around we go
for (; ; )
{
AttemptWaitForResponse:
// Clear out the pci status and flow control registers before use
PciFrame.ResetPciBytes();
if (!Form1.SIM_MODE)
{
// Read response, block here till messages arrive or timeout expired
try
{
messages = Channel.GetMessages(1, timeOut);
}
catch (Exception e)
{
Logger.WriteLineAsync($"J2534.GetMessages() error. {e.Message}");
}
// Save full length message response for our reference
pid_Obj.RxBytes = messages.Messages[0].Data;
}
else
{
// Debug: Simulate message behaviour. Basically keep pumping them out until the buffer is empty
if (dbg_RxMsgIndx < simReturnMessage.Count)
{
pid_Obj.RxBytes = simReturnMessage[dbg_RxMsgIndx];
}
else
{
pid_Obj.Status = PID.J2534_Message.status.Error;
pid_Obj.ErrorMsg = PID.J2534_Message.errorReason.BufferEmptyTimeout;
goto Exit;
}
dbg_RxMsgIndx++;
}
// Log to status console
if (Logger.LogTxRxMsgsToStatusConsole) Logger.WriteLineAsync($"{Form1.indent} Rx {BitConverter.ToString(pid_Obj.RxBytes).Replace("-", " ").Remove(0, 12)}");
// Split into can id and can data frame
(pid_Obj.CanID, pid_Obj.CanFrame) = ParseOutCanIdAndFrame(pid_Obj.RxBytes);
// Parses pci frame(s) information
PciFrame.ePciType pciType = PciFrame.GetPciFrames(pid_Obj.CanFrame);
switch (pciType)
{
case PciFrame.ePciType.Single:
pid_Obj.DataLen = PciFrame.DataLength;
// Payload. For pci type 'single' payload width is 7 bytes
pid_Obj.CanPayload = GetPayload(pid_Obj.CanFrame, 1, pid_Obj.DataLen);
// Add the payload to the response list
pid_Obj.Payload.Add(pid_Obj.CanPayload);
// Positive response
if (pid_Obj.CanFrame[1] == 0x6D) // Success, all done
{
pid_Obj.Status = PID.J2534_Message.status.OK;
goto Exit;
}
// Negative response
if (pid_Obj.CanFrame[1] == 0x7F) // Negative response
{
bool error = ProcessNegativeResponse(pid_Obj, ref negativeResponse, pid_Obj.MaxRetries);
if (error)
goto Exit; // No good, exit with error
else
goto AttemptWaitForResponse; // Response pending, attempt again to wait for a message to arrive
}
goto Exit;
case PciFrame.ePciType.First:
pid_Obj.DataLen = PciFrame.DataLength;
// Calc number of messages we are to receive
pid_Obj.TrgtMsgCnt = NumberOfRxMsgs(pid_Obj.DataLen);
// This is the first message
pid_Obj.ActMsgCnt++;
// Payload. For pci type 'first' payload width is 6 bytes
pid_Obj.CanPayload = GetPayload(pid_Obj.CanFrame, 2, 6);
// Add the payload to the response list
pid_Obj.Payload.Add(pid_Obj.CanPayload);
break;
case PciFrame.ePciType.Consecutive:
pid_Obj.SeqNum = PciFrame.SequenceNum;
// The sequence numbers start at 0x21 through to 0x2F then it repeats. Anyway, I'm not
// looking at them, just keeping count instead
pid_Obj.ActMsgCnt++;
// Payload. For pci type 'consecutive' payload width is 7 bytes
pid_Obj.CanPayload = GetPayload(pid_Obj.CanFrame, 1, 7);
// Add the payload to the response list
pid_Obj.Payload.Add(pid_Obj.CanPayload);
// Last message?
if (pid_Obj.TrgtMsgCnt == pid_Obj.ActMsgCnt)
{
pid_Obj.Status = PID.J2534_Message.status.OK;
goto Exit;
}
break;
case PciFrame.ePciType.FlowControl:
PciFrame.eFlowCtrl flowCtrl = PciFrame.GetFlowCtrlCode(pid_Obj.CanFrame);
switch (flowCtrl)
{
case PciFrame.eFlowCtrl.ClearToSend:
goto SendNextMsg;
case PciFrame.eFlowCtrl.Wait:
if (retryCnt_FlowCtrl > pid_Obj.MaxRetries)
{
pid_Obj.Status = PID.J2534_Message.status.Error;
pid_Obj.ErrorMsg = PID.J2534_Message.errorReason.FlowControlWaitTimeout;
goto Exit;
}
retryCnt_FlowCtrl++;
Thread.Sleep(5);
goto AttemptWaitForResponse;
case PciFrame.eFlowCtrl.Overflow:
pid_Obj.Status = PID.J2534_Message.status.Error;
pid_Obj.ErrorMsg = PID.J2534_Message.errorReason.FlowControlOverflow;
goto Exit;
}
break;
}
}
SendNextMsg:;
}
}
catch (Exception ex)
{
Logger.WriteLineAsync($"SendCommand Error source: {0} {ex.Source}");
APIFactory.StaticDispose();
pid_Obj.Status = PID.J2534_Message.status.Error;
pid_Obj.ErrorMsg = PID.J2534_Message.errorReason.Exception;
pid_Obj.ExceptionMsg = ex.Message;
}
Exit:
// If error, print result
if (pid_Obj.Status == PID.J2534_Message.status.Error)
{
Logger.WriteLineAsync
(
$"{Form1.indent} Send command error: " +
$"{pid_Obj.ErrorMsg.ToString()} " +
$"{(pid_Obj.NegResp == PID.J2534_Message.negResponce.None ? String.Empty : pid_Obj.NegResp.ToString())}"
);
if (pid_Obj.ErrorMsg == PID.J2534_Message.errorReason.Exception)
{
Logger.WriteLineAsync($"{Form1.indent} Exception message: {pid_Obj.ExceptionMsg.ToString()}");
}
}
}
private static bool ProcessNegativeResponse(PID.Pid_Obj pid, ref int retryCount, int maxRetries)
{
// 7. Negative Response ($7F) Service Definition pg 69
bool error = false;
byte errorCode = pid.CanFrame[3];
switch (errorCode)
{
case 0x11:
pid.Status = PID.J2534_Message.status.Error;
pid.ErrorMsg = PID.J2534_Message.errorReason.NegativeResponse;
pid.NegResp = PID.J2534_Message.negResponce.ServiceNotSupported;
error = true;
break;
case 0x12:
pid.Status = PID.J2534_Message.status.Error;
pid.ErrorMsg = PID.J2534_Message.errorReason.NegativeResponse;
pid.NegResp = PID.J2534_Message.negResponce.SubFunctionNotSupported_InvalidFormat;
error = true;
break;
case 0x22:
pid.Status = PID.J2534_Message.status.Error;
pid.ErrorMsg = PID.J2534_Message.errorReason.NegativeResponse;
pid.NegResp = PID.J2534_Message.negResponce.ConditionsNotCorrectOrRequestSequenceError;
error = true;
break;
case 0x31:
pid.Status = PID.J2534_Message.status.Error;
pid.ErrorMsg = PID.J2534_Message.errorReason.NegativeResponse;
pid.NegResp = PID.J2534_Message.negResponce.RequestOutOfRange;
error = true;
break;
case 0x35:
pid.Status = PID.J2534_Message.status.Error;
pid.ErrorMsg = PID.J2534_Message.errorReason.NegativeResponse;
pid.NegResp = PID.J2534_Message.negResponce.InvalidKey;
error = true;
break;
case 0x36:
pid.Status = PID.J2534_Message.status.Error;
pid.ErrorMsg = PID.J2534_Message.errorReason.NegativeResponse;
pid.NegResp = PID.J2534_Message.negResponce.ExceedNumberOfAttempts;
error = true;
break;
case 0x37:
pid.Status = PID.J2534_Message.status.Error;
pid.ErrorMsg = PID.J2534_Message.errorReason.NegativeResponse;
pid.NegResp = PID.J2534_Message.negResponce.RequiredTimeDelayNotExpired;
error = true;
break;
case 0x78:
error = false;
retryCount++;
if (retryCount > maxRetries)
{
pid.Status = PID.J2534_Message.status.Error;
pid.ErrorMsg = PID.J2534_Message.errorReason.NegativeResponse;
pid.NegResp = PID.J2534_Message.negResponce.RequestCorrectlyReceived_ResponsePending;
error = true;
}
break;
case 0x81:
pid.Status = PID.J2534_Message.status.Error;
pid.ErrorMsg = PID.J2534_Message.errorReason.NegativeResponse;
pid.NegResp = PID.J2534_Message.negResponce.SchedulerFull;
error = true;
break;
case 0x83:
pid.Status = PID.J2534_Message.status.Error;
pid.ErrorMsg = PID.J2534_Message.errorReason.NegativeResponse;
pid.NegResp = PID.J2534_Message.negResponce.VoltageOutOfRangeFault;
error = true;
break;
case 0x85:
pid.Status = PID.J2534_Message.status.Error;
pid.ErrorMsg = PID.J2534_Message.errorReason.NegativeResponse;
pid.NegResp = PID.J2534_Message.negResponce.GeneralProgrammingFailure;
error = true;
break;
case 0x89:
pid.Status = PID.J2534_Message.status.Error;
pid.ErrorMsg = PID.J2534_Message.errorReason.NegativeResponse;
pid.NegResp = PID.J2534_Message.negResponce.DeviceTypeError;
error = true;
break;
case 0x99:
pid.Status = PID.J2534_Message.status.Error;
pid.ErrorMsg = PID.J2534_Message.errorReason.NegativeResponse;
pid.NegResp = PID.J2534_Message.negResponce.ReadyForDownload_DTCStored;
error = true;
break;
case 0xE3:
pid.Status = PID.J2534_Message.status.Error;
pid.ErrorMsg = PID.J2534_Message.errorReason.NegativeResponse;
pid.NegResp = PID.J2534_Message.negResponce.DeviceControlLimitsExceeded;
error = true;
break;
default:
pid.Status = PID.J2534_Message.status.Error;
pid.ErrorMsg = PID.J2534_Message.errorReason.NegativeResponse;
pid.NegResp = PID.J2534_Message.negResponce.UndocumentedResponseCode;
error = true;
break;
}
return error;
}