//-----------------------------------------------------------------------
//
// Copyright © 2012 Nils Hammar. All rights reserved.
//
//-----------------------------------------------------------------------
/*
* Software to access vehicle information via the OBD-II connector.
*
* Copyright © 2012 Nils Hammar
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Alternative licensing is possible, see the licensing document.
*
* The above text may not be removed or modified.
*/
namespace Protocol
{
using System.Threading;
using global::DeviceApi;
using global::SharedObjects;
using global::SharedObjects.Api;
using global::SharedObjects.GUI;
using global::SharedObjects.Misc;
using global::SharedObjects.Protocol;
///
/// Handler for ISO9141, ISO14230 and KWP2000
///
/// UNDER DEVELOPMENT!
///
///
public class Protocol_ISO14230 : Protocol_Common, IProtocolHandler, IMsgCallback
{
///
/// Start Communication.
///
private const int SERVICEID_STARTCOMM = 0x81;
///
/// Stop Communication.
///
private const int SERVICEID_STOPCOMM = 0x82;
///
/// Get Timing Parameters.
///
private const int SERVICEID_GET_TIMING_PARAMS = 0x83;
///
/// Negative Response. (usually handled by mode parser).
///
private const int SERVICEID_NRC = 0x7F; // Same NRC as for other requests.
///
/// Start Communication Response.
///
private const int SERVICEID_STARTCOMM_RESPONSE = 0xC1;
///
/// Stop Communication Response.
///
private const int SERVICEID_STOPCOMM_RESPONSE = 0xC2;
///
/// Get Timing Parameters Response.
///
private const int SERVICEID_GET_TIMING_PARAMS_RESPONSE = 0xC3;
///
/// Protocol not yet detected.
///
private const int PROTOCOL_UNKNOWN = 0x00;
///
/// Protocol ISO9141 detected.
///
private const int PROTOCOL_ISO9141 = 0x01;
///
/// Protocol ISO14230 (KWP2000) detected.
///
private const int PROTOCOL_ISO14230 = 0x02;
///
/// Possible address bit alternatives for protocol.
///
private static readonly string[] possibleAddressbits = { "8" };
/*
///
/// Array of protocol names.
///
private static readonly string[] possibleProtocols =
{
"Unknown",
"ISO9141",
"ISO14230"
};
*/
/*
///
/// Header modes for data.
///
private static readonly string[] headerModes =
{
"HM0: ",
"HM1: ",
"HM2: Physical Target Address",
"HM3: Functional Target Address"
};
*/
///
/// Message ID for keep alive message.
///
private int keepAliveMsgId;
///
/// Gets Which protocol that was detected.
///
public int detectedProtocol { get; private set; }
///
/// Gets or sets a value indicating whether the long header shall be used even for short messages.
///
public bool forceLongHeader { get; set; }
///
/// Initializes a new instance of the class.
///
/// Logging interface.
/// Current protocol.
/// Current message handler instance.
/// Source address.
/// Checksum flag.
/// Callback interface for enabling the buttons when a protocol is fully initialized.
public Protocol_ISO14230(
ILogging iLogging,
Protocols protocol,
MessageHandler messageHandler,
uint srcAddr,
bool checksum,
IEnableButtons iEnableButtons)
: base(iLogging, protocol, messageHandler, srcAddr, checksum, iEnableButtons)
{
this.iLogging.appendText("Protocol_ISO14230 & ISO9141: srcAddr=0x" + srcAddr.ToString("x2") + ", checksum=" + checksum + "\r\n");
this.detectedProtocol = PROTOCOL_UNKNOWN;
this.forceLongHeader = false;
// Register this protocol handler with the message handler.
this.messageHandler.setProtocolCallback(this);
}
///
/// Get the address bit alternatives for the protocol.
///
/// Array of alternatives.
public static string[] addressBitAlternatives()
{
return possibleAddressbits;
}
///
/// Indicates if protocol is in 29 bit mode.
///
/// 'true' if protocol is in 29 bit mode.
public bool is29bit()
{
return false;
}
///
/// Initialize protocol.
///
/// TX Flags for any messages that are created during initialization.
public void init(int txFlags)
{
uint outBytes = 0;
uint keyWord1 = 0;
uint keyWord2 = 0;
string errtext;
PassThruConstants.resultCode ret = PassThruConstants.resultCode.ERR_SUCCESS;
if (ret != 0 || (keyWord1 == 0 && keyWord2 == 0))
{
iLogging.appendText("init(FIVE_BAUD_INIT)\r\n");
ret = this.messageHandler.IoctlFiveBaudInit(0x33, out outBytes, out keyWord1, out keyWord2);
iLogging.appendText("FIVE_BAUD_INIT: ret=" + ret + "[" + J2534_Error.getErrorName(ret) + "], outBytes=" + outBytes + ", kw1=0x" + keyWord1.ToString("x2") + ", kw2=0x" + keyWord2.ToString("x2") + "\r\n");
}
if (this.protocol.id == Protocols.ISO14230 && (ret != 0 || (keyWord1 == 0 && keyWord2 == 0)))
{
Thread.Sleep(400);
iLogging.appendText("init(FAST_INIT)\r\n");
IPassThruMsg result;
ret = this.messageHandler.IoctlFastInit(this.protocol.id, this.srcAddr, out result);
iLogging.appendText("FAST_INIT: ret=" + ret + "[" + J2534_Error.getErrorName(ret) + "]\r\n");
iLogging.dispMsg("FAST_INIT", result);
this.messageHandler.errTestConnection(ret, out errtext);
iLogging.appendText(errtext + "\r\n");
}
if (this.messageHandler.errTestConnection(ret, out errtext))
{
uint keyword = keyWord1 << 8 | keyWord2;
switch (keyword)
{
case 0x0808:
case 0x9494:
this.detectedProtocol = PROTOCOL_ISO9141;
this.iLogging.appendText("ISO 9141 detected.\r\n");
this.start();
break;
case 0x8fe9:
case 0x8f6b:
case 0x8f6d:
case 0x8fef:
this.detectedProtocol = PROTOCOL_ISO14230;
this.iLogging.appendText("ISO 14230 detected.\r\n");
this.start();
break;
default:
this.iLogging.appendText("Unsupported keyword: 0x" + keyword.ToString("x4") + ".\r\n");
break;
}
}
else
{
iLogging.appendText(errtext + "\r\n");
throw new ProtocolInitException("Connection Failed.");
}
}
///
/// Start protocol.
///
public void start()
{
IPassThruMsg msg = PassThruMsg.getMaskedMsg(this.protocol);
if (this.detectedProtocol == PROTOCOL_ISO9141)
{
iLogging.appendText("start(PROTOCOL_ISO9141)\r\n");
// addPayload(new byte[] { 0x68, 0x6a, 0xf0, SERVICEID_STARTCOMM }, ref msg);
// addPayload(new byte[] { 0x68, 0x6a, 0x3f, SERVICEID_STARTCOMM }, ref msg);
addPayload(new byte[] { 0x68, 0x6a, 0xf1, SERVICEID_STARTCOMM }, ref msg);
}
else
{
iLogging.appendText("start(PROTOCOL_ISO14230)\r\n");
addPayload(new byte[] { 0xc1, 0x33, 0xf1, SERVICEID_STARTCOMM }, ref msg);
}
PassThruConstants.resultCode res = this.messageHandler.startPeriodicMessage(msg, out this.keepAliveMsgId);
this.iLogging.appendText("Keep alive msg started, res=" + res + ", msgId=" + this.keepAliveMsgId + "\r\n");
this.iEnableButtons.enableButtons();
}
///
/// Stop protocol.
///
public void stopProtocol()
{
this.messageHandler.stopPeriodicMessage(this.keepAliveMsgId);
}
///
/// Receive one message and dispatch it to receivers.
///
/// Message to receive.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Reviewed.")]
public void receiveMessage(IPassThruMsg rxmsg)
{
int headerMode;
int dataLength;
int dataStart;
int destination;
int source;
getHeaderData(rxmsg, out headerMode, out dataLength, out dataStart, out destination, out source);
// Calculate Checksum if the checkbox is checked.
// Additional flags may need to be checked too.
int expectedCs = 0;
int calculatedCs = 0;
if (this.checksum)
{
expectedCs = (rxmsg.Data[rxmsg.DataSize - 1] & 0xff);
calculatedCs = Utils.getChecksum(rxmsg.Data, 0, rxmsg.DataSize - 1);
if (expectedCs != calculatedCs)
{
this.iLogging.appendText("Checksum mismatch detected: expectedCs=" + expectedCs + ", calculatedCs=" + calculatedCs);
}
}
// Get the Service Identification.
if (dataStart < rxmsg.DataSize)
{
// int sid = rxmsg.Data[dataStart];
/*
this.iLogging.appendText(headerModes[headerMode]
+ ", From=0x" + source.ToString("x2")
+ ", To=0x" + destination.ToString("x2")
+ ", dataStart=" + dataStart
+ ", dataLength=" + dataLength
+ ", Sid=0x" + sid.ToString("x2")
+ ", expectedCs=0x" + expectedCs.ToString("x2")
+ ", calculatedCs=0x" + calculatedCs.ToString("x2")
+ "\r\n"
);
this.iLogging.dispMsg("Rx", rxmsg);
*/
// Extract the payload.
byte[] payload = extractPayload(rxmsg, dataStart);
this.dispatchMessage(rxmsg, payload, (uint)source, 0);
}
else
{
this.iLogging.appendText("Data start is beyond end of received data.\r\n", LogLevel.LOG_WARN);
}
}
///
/// Get payload part from message.
///
/// Message to get payload data from.
/// Payload byte array.
public override byte[] getPayload(IPassThruMsg msg)
{
int headerMode;
int dataLength;
int dataStart;
int destination;
int source;
getHeaderData(msg, out headerMode, out dataLength, out dataStart, out destination, out source);
byte[] payload = extractPayload(msg, dataStart);
return payload;
}
///
/// Send one message where the header info already is set in the payload.
///
/// Source address (address of tester).
/// Tx Flags
/// Message Payload which includes address data.
public void sendProbeMessage(uint srcAddr, int txFlags, byte[] payload)
{
if (this.detectedProtocol == PROTOCOL_ISO9141)
{
this.sendMessage(0x3f, 0x33, txFlags, payload);
}
else
{
this.sendMessage(0xf0, 0x33, txFlags, payload);
}
//// sendMessage(0xf1, 0x33, txFlags, payload);
}
///
/// Send one message to the given receiver address.
///
/// Source address.
/// Destination address.
/// TX Flags.
/// Payload data.
/// Error message in case of a problem.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "3", Justification = "Reviewed.")]
public string sendMessage(uint sourceAddress, uint destinationAddress, int txFlags, byte[] payload)
{
IPassThruMsg txmsg = PassThruMsg.getMaskedMsg(this.protocol);
bool functionalTargetAddressing = false;
switch (this.detectedProtocol)
{
case PROTOCOL_ISO9141:
destinationAddress = 0x6a;
break;
case PROTOCOL_ISO14230:
functionalTargetAddressing = (destinationAddress == 0x33);
break;
}
// Add Address.
this.setAddress(ref txmsg, sourceAddress, destinationAddress); // FIXME!
this.setHeader(ref txmsg, (uint)payload.Length, functionalTargetAddressing);
addPayload(payload, ref txmsg);
PassThruConstants.resultCode res = this.sendMessage(txFlags, txmsg);
// Check if error.
string errtext = string.Empty;
if (!this.messageHandler.errTestConnection(res, out errtext))
{
this.iLogging.dispMsg("Err", txmsg);
}
return errtext;
}
///
/// Set additional header data in addition to the address.
///
/// Message to add data to.
/// Size of message.
/// 'true' if the destination address is functional address.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Reviewed.")]
public void setHeader(ref IPassThruMsg txmsg, uint size, bool functionalTargetAddressing)
{
switch (this.detectedProtocol)
{
case PROTOCOL_ISO9141:
txmsg.Data[0] = 0x68;
txmsg.DataSize = 3;
break;
case PROTOCOL_ISO14230:
this.setISO14230Header(ref txmsg, size, functionalTargetAddressing);
break;
}
}
///
/// Set the address(es) of the message.
///
/// Message to set data in.
/// Source Address.
/// Destination Address.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Reviewed.")]
public void setAddress(ref IPassThruMsg txmsg, uint sourceAddress, uint destinationAddress)
{
txmsg.Data[1] = (byte)(destinationAddress & 0xff);
txmsg.Data[2] = (byte)(sourceAddress & 0xff);
txmsg.DataSize = 3;
}
///
/// Set the address(es) of the message.
///
/// This is a fully composed address, usually used by filters.
///
///
/// Message to set data in.
/// Combined Address.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Reviewed.")]
public void setAddress(ref IPassThruMsg txmsg, uint address)
{
txmsg.Data[1] = (byte)((address >> 8) & 0xff);
txmsg.Data[2] = (byte)(address & 0xff);
txmsg.DataSize = 3;
}
///
/// Get header data from message.
///
/// Message to get header data from.
/// Header mode value.
/// Data length.
/// Start position for data.
/// Destination address.
/// Source address.
private static void getHeaderData(IPassThruMsg rxmsg, out int headerMode, out int dataLength, out int dataStart, out int destination, out int source)
{
headerMode = ((rxmsg.Data[0] >> 6) & 0xff);
dataLength = (rxmsg.Data[0] & 0x3f);
dataStart = 3;
// If the length was zero we have a 4 byte header with the length
// in the 4th byte.
if (dataLength == 0)
{
dataLength = (rxmsg.Data[3] & 0xff);
dataStart = 4;
}
destination = rxmsg.Data[1]; // Should be 'our' address when we are receiving.
source = rxmsg.Data[2]; // Should be ECU address when we are receiving.
}
///
/// Add the payload to the message.
///
/// Byte array of payload to send.
/// Message to update with payload.
/// Hex string with message, to be used for logging.
private static string addPayload(byte[] payload, ref IPassThruMsg txmsg)
{
string dataStr = string.Empty;
for (int i = 0; i < payload.Length; i++)
{
txmsg.Data[txmsg.DataSize++] = payload[i];
dataStr += string.Format("{0:x2} ", payload[i]);
}
return dataStr;
}
///
/// Call the message handler and send the formatted message structure.
///
/// TX Flags.
/// Message to send.
/// Status code, 0=success.
private PassThruConstants.resultCode sendMessage(int txFlags, IPassThruMsg txmsg)
{
// Set message flags.
txmsg.TxFlags = txFlags;
this.addChecksum(ref txmsg);
return this.messageHandler.sendMessage(txmsg);
}
///
/// Add checksum if requested.
/// Notice that CAN and ISO 15765 doesn't need a checksum.
/// But it's up to the user to decide, so we just check
/// if the checkbox is checked.
///
/// Message to add checksum to.
private void addChecksum(ref IPassThruMsg txmsg)
{
if (this.checksum)
{
int cs = Utils.getChecksum(txmsg.Data, 0, txmsg.DataSize);
txmsg.Data[txmsg.DataSize++] = (byte)(cs & 0xff);
}
}
///
/// Set additional ISO14230 header data in addition to the address.
///
/// Message to add data to.
/// Size of message.
/// 'true' if the destination address is functional address.
private void setISO14230Header(ref IPassThruMsg txmsg, uint size, bool functionalTargetAddressing)
{
int headerMode;
if (functionalTargetAddressing)
{
headerMode = 3;
}
else
{
headerMode = 2;
}
txmsg.Data[0] = (byte)((headerMode << 6) & 0xc0);
if (size >= 64 || this.forceLongHeader)
{
txmsg.Data[3] = (byte)(size & 0xff);
txmsg.DataSize = 4;
}
else
{
txmsg.Data[0] |= (byte)(size & 0x3f);
txmsg.DataSize = 3;
}
}
}
}