//-----------------------------------------------------------------------
//
// 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 DeviceApi.J2534
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using global::SharedObjects;
using global::SharedObjects.Api;
using global::SharedObjects.CAN;
using global::SharedObjects.CAN.Objects;
using global::SharedObjects.DataMgmt;
using global::SharedObjects.Protocol;
using global::VectorComm;
///
/// TODO: Update summary.
///
public class VectorPassthru : IPassThru, IPresentationLayer, IDisposable
{
///
/// Supported connection flags by the device.
///
private static CanFlags[] connFlags = new CanFlags[]
{
new CanFlags(PassThruConstants.connflags.SNIFF_MODE, "SNIFF_MODE", true),
new CanFlags(PassThruConstants.connflags.CAN_ID_BOTH, "CAN_ID_BOTH", true),
new CanFlags(PassThruConstants.connflags.CAN_29BIT_ID, "CAN_29BIT_ID", false),
};
///
/// Supported filter flags by the device.
///
private static CanFlags[] messageFlags = new CanFlags[]
{
new CanFlags(PassThruConstants.txflags.CAN_29BIT_ID, "CAN_29BIT_ID", false),
new CanFlags(PassThruConstants.txflags.ISO15765_ADDR_TYPE, "ISO15765_ADDR_TYPE", false),
new CanFlags(PassThruConstants.txflags.ISO15765_FRAME_PAD, "ISO15765_FRAME_PAD", true),
};
///
/// Gets the name of the interface.
///
public string name
{
get
{
return "Vector CAN Passthru emulation";
}
}
///
/// Gets the code name of the interface.
///
/// For dynamic interfaces this is the same as the DLL filename without extension.
///
///
public string code
{
get
{
return "CANEmul";
}
}
///
/// To return an unique device ID for each opening of the device.
///
private int DeviceIdCounter = 1;
///
/// To return an unique connection ID for each opening of the device.
///
private int ChannelIdCounter = 1;
///
/// To return an unique filter ID for each opening of the device.
///
private int FilterIdCounter = 1;
///
/// Dictionary of open devices.
///
private Dictionary> openDevices = new Dictionary>();
///
/// Dictionary of open channels.
///
private Dictionary openChannels = new Dictionary();
///
/// Dictionary of active filters.
///
private Dictionary activeFilters = new Dictionary();
///
/// Last error encountered.
///
private string lastError = "No Error";
///
/// CAN communicator instance.
///
private VectorCanCommunicator vectorCanCommunicator;
///
/// Session layer part.
///
private SessionLayer sessionLayer;
///
/// Event logging instance.
///
private ILogging iLogging;
///
/// Preferences instance.
///
private IPreferences iPreferences;
///
/// Queue of incoming data.
///
private Queue inQueue = new Queue();
///
/// Initializes a new instance of the class.
///
/// Event logging instance.
/// Preferences instance.
public VectorPassthru(ILogging iLogging, IPreferences iPreferences)
{
this.iLogging = iLogging;
this.iPreferences = iPreferences;
this.vectorCanCommunicator = new VectorCanCommunicator("CanDataMiner", iLogging, 20);
}
///
/// Clean up.
///
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
///
/// Process the payload data.
///
/// Target address.
/// Data length.
/// Payload data.
public void processPayload(uint address, uint length, byte[] data)
{
lock (this.inQueue)
{
this.inQueue.Enqueue(new PayloadItem(address, length, data));
Monitor.PulseAll(this.inQueue);
}
}
///
/// Get list of supported protocols.
///
/// Array of supported protocols by the device.
public Protocols[] getProtocols()
{
return new Protocols[]
{
Protocols.getProtocolById(Protocols.ISO15765),
Protocols.getProtocolById(Protocols.CAN),
};
}
///
/// Indicate if the interface is a serial device.
///
/// Notice that this also applies to Serial on USB devices.
///
///
/// 'true' if serial device.
public bool isSerial()
{
return false;
}
///
/// Flag indicating that the interface has a disconnect bug that needs to be considered.
///
/// 'true' if bug exists.
public bool disconnectBug()
{
return false;
}
///
/// Get array of connection flags available on the device.
///
/// Array of CanFlags.
public CanFlags[] getConnFlags()
{
return connFlags;
}
///
/// Get array of message flags available on the device.
///
/// Array of CanFlags.
public CanFlags[] getMessageFlags()
{
return messageFlags;
}
///
/// Implementation of PassThruOpen.
///
/// Always 'null'.
/// Pointer to selected device ID. (return value)
/// '0' if success, non-zero if a failure occurred.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "1", Justification = "Reviewed, intentional.")]
public unsafe PassThruConstants.resultCode PassThruOpen(void* pName, int* pDeviceID)
{
PassThruConstants.resultCode res = PassThruConstants.resultCode.ERR_DEVICE_NOT_CONNECTED;
try
{
this.sessionLayer = new SessionLayer(this.iLogging, this.iPreferences, 0x07E8, 0x07F8, true);
this.sessionLayer.addPresentationLayer(this);
this.sessionLayer.setCanCommunicator(this.vectorCanCommunicator);
this.vectorCanCommunicator.startCan();
this.openDevices.Add(this.DeviceIdCounter, new List());
*pDeviceID = this.DeviceIdCounter++;
res = PassThruConstants.resultCode.ERR_SUCCESS;
}
catch (Exception ex)
{
this.iLogging.appendTextLn(ex.GetType().ToString() + ": " + ex.Message);
}
return res;
}
///
/// Close the given device.
///
/// ID of device to close.
/// '0' if success, non-zero if a failure occurred.
public PassThruConstants.resultCode PassThruClose(int DeviceID)
{
if (this.openDevices.ContainsKey(DeviceID))
{
if (this.sessionLayer != null)
{
this.vectorCanCommunicator.stopCan();
this.sessionLayer.setCanCommunicator(null);
this.sessionLayer.removePresentationLayer(this);
this.sessionLayer.Dispose();
this.sessionLayer = null;
}
List deviceChannels = this.openDevices[DeviceID];
foreach (SimulatedChannel channel in deviceChannels)
{
this.PassThruDisconnect(channel.channelId);
}
this.openDevices.Remove(DeviceID);
// Remove remaining data from in queue.
this.inQueue.Clear();
return PassThruConstants.resultCode.ERR_SUCCESS;
}
return PassThruConstants.resultCode.ERR_INVALID_DEVICE_ID;
}
///
/// Establish a connection on the given device.
///
/// ID of current device.
/// ID of protocol to use.
/// Connection Flags.
/// Baudrate to use for connection.
/// Pointer to value for channel id (return value)
/// '0' if success, non-zero if a failure occurred.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "4", Justification = "Reviewed, intentional.")]
public unsafe PassThruConstants.resultCode PassThruConnect(int DeviceID, int ProtocolID, uint Flags, uint Baudrate, int* pChannelID)
{
if (this.openDevices.ContainsKey(DeviceID))
{
// Only CAN-bus handled.
if (ProtocolID != Protocols.ISO15765 && ProtocolID != Protocols.CAN)
{
return PassThruConstants.resultCode.ERR_INVALID_PROTOCOL_ID;
}
Protocols protocol = Protocols.getProtocolById(ProtocolID);
foreach (SimulatedChannel channel in this.openChannels.Values)
{
Protocols proto1 = Protocols.getProtocolById(channel.protocol.id);
if (proto1.families == protocol.families)
{
return PassThruConstants.resultCode.ERR_CHANNEL_IN_USE;
}
}
List deviceChannels = this.openDevices[DeviceID];
SimulatedChannel simulatedConnection = new SimulatedChannel(DeviceID, this.ChannelIdCounter, protocol);
deviceChannels.Add(simulatedConnection);
*pChannelID = this.ChannelIdCounter;
this.openChannels.Add(this.ChannelIdCounter++, simulatedConnection);
return PassThruConstants.resultCode.ERR_SUCCESS;
}
return PassThruConstants.resultCode.ERR_DEVICE_NOT_CONNECTED;
}
///
/// Disconnect the given channel.
///
/// ID of channel to disconnect.
/// '0' if success, non-zero if a failure occurred.
public PassThruConstants.resultCode PassThruDisconnect(int ChannelID)
{
if (this.openChannels.ContainsKey(ChannelID))
{
SimulatedChannel channel = this.openChannels[ChannelID];
List deviceChannels = this.openDevices[channel.deviceId];
if (deviceChannels.Contains(channel))
{
deviceChannels.Remove(channel);
}
this.openChannels.Remove(ChannelID);
return PassThruConstants.resultCode.ERR_SUCCESS;
}
return PassThruConstants.resultCode.ERR_INVALID_CHANNEL_ID;
}
///
/// Read messages from the given channel.
///
/// ID of channel to read from.
/// Pointer to message(s) read.
/// Number of messages to read, value set at return to actual number read.
/// Timeout value before returning with no message read.
/// '0' if success, non-zero if a failure occurred.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "1", Justification = "Reviewed")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "2", Justification = "Reviewed")]
public unsafe PassThruConstants.resultCode PassThruReadMsgs(int ChannelID, PASSTHRU_MSG* pMsg, int* pNumMsgs, int Timeout)
{
if (pMsg != null)
{
if (pNumMsgs != null)
{
if (this.openChannels.ContainsKey(ChannelID))
{
lock (this.inQueue)
{
if (this.inQueue.Count == 0)
{
if (!Monitor.Wait(this.inQueue, Timeout))
{
if (this.inQueue.Count > 0)
{
this.iLogging.appendTextLn("VectorPassthru.PassThruReadMsgs(): Timeout with data present. this.sendQueue.Count=" + this.inQueue.Count);
}
}
}
if (this.inQueue.Count > 0)
{
PayloadItem item = this.inQueue.Dequeue();
pMsg->DataSize = item.passthruData.Length;
for (int i = 0; i < pMsg->DataSize; i++)
{
pMsg->Data[i] = item.passthruData[i];
}
pMsg->ExtraDataIndex = 0;
pMsg->RxStatus = 0;
pMsg->Timestamp = (int)(DateTime.Now.Ticks / TimeSpan.TicksPerSecond);
pMsg->TxFlags = 0;
*pNumMsgs = 1;
}
else
{
*pNumMsgs = 0;
return PassThruConstants.resultCode.ERR_TIMEOUT;
}
}
return PassThruConstants.resultCode.ERR_SUCCESS;
}
return PassThruConstants.resultCode.ERR_INVALID_CHANNEL_ID;
}
}
return PassThruConstants.resultCode.ERR_NULL_PARAMETER;
}
///
/// Write messages to the given channel.
///
/// ID of channel to write to.
/// Message(s) to write.
/// Number of messages to write.
/// Timeout when writing message to out channel.
/// '0' if success, non-zero if a failure occurred.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "1", Justification = "Reviewed, intentional.")]
public unsafe PassThruConstants.resultCode PassThruWriteMsgs(int ChannelID, PASSTHRU_MSG* pMsg, int* pNumMsgs, int Timeout)
{
if (this.openChannels.ContainsKey(ChannelID))
{
uint outAddress = (uint)(pMsg->Data[0] << 24 | pMsg->Data[1] << 16 | pMsg->Data[2] << 8 | pMsg->Data[3]);
IList response = new List();
for (int i = 0; i < pMsg->DataSize - 4; i++)
{
response.Add(pMsg->Data[i + 4]);
}
if (this.sessionLayer != null)
{
this.sessionLayer.sendData(outAddress, response, (uint)response.Count);
return PassThruConstants.resultCode.ERR_SUCCESS;
}
else
{
return PassThruConstants.resultCode.ERR_NULL_PARAMETER;
}
}
return PassThruConstants.resultCode.ERR_INVALID_CHANNEL_ID;
}
///
/// Start a periodic message to send from application to vehicle.
///
/// ID of channel to write to.
/// Message(s) to write.
/// Return value: ID of message being sent.
/// Interval between each message in milliseconds.
/// '0' if success, non-zero if a failure occurred.
public unsafe PassThruConstants.resultCode PassThruStartPeriodicMsg(int ChannelID, PASSTHRU_MSG* pMsg, int* pMsgID, int TimeInterval)
{
if (this.openChannels.ContainsKey(ChannelID))
{
return PassThruConstants.resultCode.ERR_SUCCESS;
}
return PassThruConstants.resultCode.ERR_INVALID_CHANNEL_ID;
}
///
/// Stop a periodic message from being sent to the vehicle.
///
/// ID of channel to stop message on.
/// ID of message to stop sending.
/// '0' if success, non-zero if a failure occurred.
public PassThruConstants.resultCode PassThruStopPeriodicMsg(int ChannelID, int MsgID)
{
if (this.openChannels.ContainsKey(ChannelID))
{
return PassThruConstants.resultCode.ERR_SUCCESS;
}
return PassThruConstants.resultCode.ERR_INVALID_CHANNEL_ID;
}
///
/// Configure a message filter.
///
/// Channel to configure filter on.
/// Type of filter to configure.
/// Mask message in filter.
/// Pattern message in filter.
/// Flow control message in filter.
/// ID of filter created (out value)
/// '0' if success, non-zero if a failure occurred.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "5", Justification = "Reviewed, intentional.")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "2", Justification = "Reviewed, intentional.")]
public unsafe PassThruConstants.resultCode PassThruStartMsgFilter(
int ChannelID,
int FilterType,
PASSTHRU_MSG* pMaskMsg,
PASSTHRU_MSG* pPatternMsg,
PASSTHRU_MSG* pFlowControlMsg,
int* pMsgID)
{
if (this.openChannels.ContainsKey(ChannelID))
{
this.activeFilters.Add(
this.FilterIdCounter,
new FilterItem(
this.FilterIdCounter,
(uint)FilterType,
pMaskMsg->DataSize,
parseMask(pMaskMsg),
parseMask(pPatternMsg),
parseMask(pFlowControlMsg),
(uint)pMaskMsg->TxFlags,
pMaskMsg->ProtocolID));
*pMsgID = this.FilterIdCounter;
this.FilterIdCounter++;
return PassThruConstants.resultCode.ERR_SUCCESS;
}
return PassThruConstants.resultCode.ERR_INVALID_CHANNEL_ID;
}
///
/// Stop a message filter.
///
/// Channel to remove filter from.
/// ID of filter to remove.
/// '0' if success, non-zero if a failure occurred.
public PassThruConstants.resultCode PassThruStopMsgFilter(int ChannelID, int MsgID)
{
if (this.openChannels.ContainsKey(ChannelID))
{
if (this.activeFilters.ContainsKey(MsgID))
{
this.activeFilters.Remove(MsgID);
return PassThruConstants.resultCode.ERR_SUCCESS;
}
else
{
return PassThruConstants.resultCode.ERR_INVALID_FILTER_ID;
}
}
return PassThruConstants.resultCode.ERR_INVALID_CHANNEL_ID;
}
///
/// Configure the programming voltage pin and voltage.
///
/// ID of Device to configure.
/// Number of pin to configure.
/// Voltage to set at pin.
/// '0' if success, non-zero if a failure occurred.
public PassThruConstants.resultCode PassThruSetProgrammingVoltage(int DeviceID, int Pin, int Voltage)
{
if (this.openDevices.ContainsKey(DeviceID))
{
return PassThruConstants.resultCode.ERR_SUCCESS;
}
return PassThruConstants.resultCode.ERR_INVALID_DEVICE_ID;
}
///
/// Read API version.
///
/// ID to get version information from.
/// Firmware version (return value).
/// DLL version (return value).
/// API version (return value).
/// '0' if success, non-zero if a failure occurred.
public unsafe PassThruConstants.resultCode PassThruReadVersion(int DeviceID, byte* pFirmwareVersion, byte* pDllVersion, byte* pApiVersion)
{
if (this.openDevices.ContainsKey(DeviceID))
{
byte[] fwVer = System.Text.Encoding.ASCII.GetBytes("0.00");
byte[] dllVer = System.Text.Encoding.ASCII.GetBytes(this.vectorCanCommunicator.dllVersion);
byte[] apiVer = System.Text.Encoding.ASCII.GetBytes("04.04");
strcpy(fwVer, pFirmwareVersion);
strcpy(dllVer, pDllVersion);
strcpy(apiVer, pApiVersion);
return PassThruConstants.resultCode.ERR_SUCCESS;
}
return PassThruConstants.resultCode.ERR_INVALID_DEVICE_ID;
}
///
/// Get the last error encountered on the device.
///
/// Pointer to string with error description.
/// '0' if success, non-zero if a failure occurred.
public unsafe PassThruConstants.resultCode PassThruGetLastError(sbyte* pErrorDescription)
{
// lastError
byte[] ascii = System.Text.Encoding.ASCII.GetBytes(this.lastError);
for (int i = 0; i < ascii.Length; i++)
{
*pErrorDescription = (sbyte)ascii[i];
pErrorDescription++;
}
*pErrorDescription = 0;
return PassThruConstants.resultCode.ERR_SUCCESS;
}
///
/// Perform IOCTL operation on the device.
///
/// ID of channel to perform operation on.
/// IOCTL Operation.
/// Input parameter for operation.
/// Output parameter for operation.
/// '0' if success, non-zero if a failure occurred.
public unsafe PassThruConstants.resultCode PassThruIoctl(
int ChannelID,
int IoctlID,
void* pInput,
void* pOutput)
{
if (this.openChannels.ContainsKey(ChannelID))
{
return PassThruConstants.resultCode.ERR_SUCCESS;
}
return PassThruConstants.resultCode.ERR_INVALID_CHANNEL_ID;
}
///
/// Clean up.
///
/// 'true' if disposing.
protected virtual void Dispose(bool disposing)
{
if (this.sessionLayer != null)
{
this.sessionLayer.Dispose();
this.sessionLayer = null;
}
if (this.vectorCanCommunicator != null)
{
this.vectorCanCommunicator.Dispose();
this.vectorCanCommunicator = null;
}
}
///
/// Copy a 'string' in a byte array to a pre-allocated area designated by a pointer.
///
/// Source array.
/// Target area.
private static unsafe void strcpy(byte[] ba, byte* p)
{
int i;
for (i = 0; i < ba.Length; i++)
{
p[i] = ba[i];
}
p[i] = 0;
}
///
/// Parse the msg mask.
///
/// Pointer to struct.
/// Parsed value
private static unsafe uint parseMask(PASSTHRU_MSG* pMsg)
{
return parseMask(new PassThruMsg(pMsg));
}
///
/// Parse the msg mask.
///
/// Message instance.
/// Parsed value
private static uint parseMask(PassThruMsg msg)
{
uint mask = (uint)(msg.Data[0] << 24 | msg.Data[1] << 16 | msg.Data[2] << 8 | msg.Data[3]);
return mask;
}
///
/// Item for a simulated channel.
///
private class SimulatedChannel
{
///
/// Gets ID of device.
///
public int deviceId { get; private set; }
///
/// Gets ID of channel.
///
public int channelId { get; private set; }
///
/// Gets protocol instance.
///
public Protocols protocol { get; private set; }
///
/// Initializes a new instance of the class.
///
/// ID of device.
/// ID of channel.
/// Protocol instance.
public SimulatedChannel(int deviceId, int channelId, Protocols protocol)
{
this.deviceId = deviceId;
this.channelId = channelId;
this.protocol = protocol;
}
}
///
/// Item for a filter.
///
private class FilterItem
{
///
/// Gets ID of filter.
///
public int msgId { get; private set; }
///
/// Gets type of filter.
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Reviewed.")]
public uint filterType { get; private set; }
///
/// Gets datasize.
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Reviewed.")]
public int datasize { get; private set; }
///
/// Gets filter mask.
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Reviewed.")]
public uint mask { get; private set; }
///
/// Gets filter pattern.
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Reviewed.")]
public uint pattern { get; private set; }
///
/// Gets filter flow.
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Reviewed.")]
public uint flow { get; private set; }
///
/// Gets filter TX flags.
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Reviewed.")]
public uint txFlags { get; private set; }
///
/// Gets filter protocol ID.
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Reviewed.")]
public int protocol { get; private set; }
///
/// Initializes a new instance of the class.
///
/// Filter ID
/// Type of filter
/// Data Size
/// Filter Mask
/// Filter Pattern
/// Filter Flow
/// Filter TX flags
/// Filter protocol ID
public FilterItem(
int msgId,
uint filterType,
int datasize,
uint mask,
uint pattern,
uint flow,
uint txFlags,
int protocol)
{
this.msgId = msgId;
this.filterType = filterType;
this.datasize = datasize;
this.protocol = protocol;
this.mask = mask;
this.pattern = pattern;
this.flow = flow;
this.txFlags = txFlags;
}
///
/// Get string representation of filter.
///
/// String representation
public override string ToString()
{
#if TRACE
return "msgId=" + this.msgId + ", filterType=" + this.filterType + ", datasize=" + this.datasize + ", protocol=0x" + this.protocol.ToString("x2") + ", mask=0x" + this.mask.ToString("x4") + ", pattern=0x" + this.pattern.ToString("x4") + ", flow=0x" + this.flow.ToString("x4") + ", txFlags=0x" + this.txFlags.ToString("x4") + "\r\n";
#else
return "msgId=" + this.msgId + "\r\n";
#endif
}
}
///
/// Payload item.
///
private class PayloadItem
{
///
/// Gets Data body to send through PassThru interface.
///
public byte[] passthruData { get; private set; }
///
/// Initializes a new instance of the class.
///
/// Destination address.
/// Payload length.
/// Payload data.
public PayloadItem(uint address, uint length, byte[] data)
{
this.passthruData = new byte[length + 4];
this.passthruData[0] = (byte)((address >> 24) & 0xff);
this.passthruData[1] = (byte)((address >> 16) & 0xff);
this.passthruData[2] = (byte)((address >> 8) & 0xff);
this.passthruData[3] = (byte)(address & 0xff);
Array.Copy(data, 0, this.passthruData, 4, length);
}
}
}
}