//-----------------------------------------------------------------------
//
// 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;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO.Ports;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using global::FtdiApi;
using global::FtdiApi.Constants;
using global::FtdiApi.Exceptions;
using global::SharedObjects;
///
/// Class for communication over a simple serial device like the VAG-Comm or Tactrix 1.3 cable.
///
public sealed class SimpleSerialDevice : IDisposable
{
///
/// Start communication request.
///
/// For ISO14230 fast init.
///
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "Reviewed.")]
private static readonly byte[] START_COMM_REQ = new byte[] { 0xc1, 0x33, 0xf1, 0x81, 0x66 };
///
/// Keepalive request.
///
/// For ISO 9141.
///
///
private static readonly byte[] KEEPALIVE_REQ_1 = new byte[] { 0x68, 0x6a, 0xf1, 0x01, 0x00, 0xc4 };
///
/// Keepalive request.
///
/// For ISO ISO14230 5 baud init.
///
///
private static readonly byte[] KEEPALIVE_REQ_2 = new byte[] { 0xc1, 0x33, 0xf1, 0x3e, 0x23 };
//// private static readonly byte[] KEEPALIVE_REQ_2 = new byte[] { 0xc2, 0x33, 0xf1, 0x01, 0x00, 0xe7 };
///
/// Byte pairs that are answers on five baud init request.
///
private static readonly byte[][] EXPECT_BYTE_PAIRS = new byte[][]
{
new byte[] { 0x08, 0x08 },
new byte[] { 0x94, 0x94 },
new byte[] { 0xe9, 0x8f },
new byte[] { 0x6b, 0x8f },
new byte[] { 0x6d, 0x8f },
new byte[] { 0xef, 0x8f },
};
///
/// Bytes to expect the second time around.
///
private static readonly byte[][] EXPECT_BYTE2 = new byte[][]
{
new byte[] { 0xcc },
};
///
/// Current FTDI API instance.
///
private FTDI ftdi;
///
/// Current FTDI port.
///
private FtdiPort ftdiPort = null;
///
/// Current serial port using Windows API.
///
private SerialPort serialPort1 = null;
///
/// Lock object to avoid race condition on writing data to port.
///
private object lockObject2 = new object();
///
/// Current serial port using FTDI API.
///
private string comPort = null;
///
/// Default communication speed.
///
private int speed = 10400;
///
/// Current expect byte set.
///
private byte[][] expectByteSet = null;
///
/// Bytes received.
///
private byte[] rxBytes = null;
///
/// Initialization thread for the communication.
///
private Thread initThread;
///
/// Serial data event handler.
///
private SerialDataReceivedEventHandler serialDataReceivedEventHandler = null;
///
/// Timestamp for last read of data.
///
private long lastReadTs = 0;
///
/// Index of received data.
///
private int rxIx = 0;
///
/// Length of expected bytes.
///
private int expectByteLength = 0;
///
/// Delay for five baud init.
///
private int newDelay = 200;
///
/// Text logging instance.
///
private ILogging iTextLogging;
///
/// Object for synchronization between threads.
///
private object lockObject = new object();
///
/// Wait index value, indicates how the connection handshake progress is.
///
private int wIndex = 0;
///
/// Delta time between incoming characters.
///
private long delta = 0;
///
/// Keepalive thread control flag.
///
private bool doRun = true;
///
/// Characters to discard.
///
private int discardChars = 0;
///
/// Event handling for FTDI API.
///
private AutoResetEvent receivedDataEvent;
///
/// Data receiver worker instance.
///
private BackgroundWorker dataReceivedHandler;
///
/// Flag indicating that the FTDI api is valid.
///
private bool ftdiValidData = true;
///
/// Flag indicating that a Break was received.
///
private bool breakFlag = false;
///
/// Request to send flag.
///
private bool rtsFlag = false;
///
/// Data Terminal Ready flag.
///
private bool dtrFlag = false;
///
/// Expectation stage ID.
///
private int expectStage = 0;
///
/// Previous modem status, used to detect changes of signals.
///
private byte oldModemStatus = 0;
///
/// Flag indicating if connection is established or not.
///
private bool connected = false;
///
/// Gets or sets a value indicating whether the L-Line shall be used.
///
public bool lLine { get; set; }
///
/// Gets or sets a value indicating whether calibration shall be done.
///
public bool calibrate { get; set; }
///
/// Gets a value indicating whether the Carrier Detect state is active.
///
public bool cdState
{
get
{
if (this.ftdiPort != null)
{
byte mdmStat = 0;
this.ftdiPort.GetModemStatus(ref mdmStat);
return ((mdmStat & FT_MODEM_STATUS.FT_DCD) != 0);
}
return this.serialPort1.CDHolding;
}
}
///
/// Gets a value indicating whether the Clear to Send state is active.
///
public bool ctsState
{
get
{
if (this.ftdiPort != null)
{
byte mdmStat = 0;
this.ftdiPort.GetModemStatus(ref mdmStat);
return ((mdmStat & FT_MODEM_STATUS.FT_CTS) != 0);
}
return this.serialPort1.CtsHolding;
}
}
///
/// Gets a value indicating whether the Data Set Ready state is active.
///
public bool dsrState
{
get
{
if (this.ftdiPort != null)
{
byte mdmStat = 0;
this.ftdiPort.GetModemStatus(ref mdmStat);
return ((mdmStat & FT_MODEM_STATUS.FT_DSR) != 0);
}
return this.serialPort1.DsrHolding;
}
}
///
/// Gets or sets a value indicating whether Request to Send state shall be active.
///
public bool rtsState
{
get
{
if (this.ftdiPort != null)
{
return this.rtsFlag;
}
return this.serialPort1.RtsEnable;
}
set
{
this.rtsFlag = value;
if (this.ftdiPort != null)
{
this.ftdiPort.SetRTS(this.rtsFlag);
}
else
{
this.serialPort1.RtsEnable = this.rtsFlag;
}
}
}
///
/// Gets or sets a value indicating whether Data Terminal Ready state shall be active.
///
public bool dtrState
{
get
{
if (this.ftdiPort != null)
{
return this.dtrFlag;
}
return this.serialPort1.DtrEnable;
}
set
{
this.dtrFlag = value;
if (this.ftdiPort != null)
{
this.ftdiPort.SetDTR(this.dtrFlag);
}
else
{
this.serialPort1.DtrEnable = this.dtrFlag;
}
}
}
///
/// Gets or sets a value indicating whether Break state shall be active.
///
public bool breakState
{
get
{
if (this.ftdiPort != null)
{
return this.breakFlag;
}
return this.serialPort1.BreakState;
}
set
{
this.breakFlag = value;
if (this.ftdiPort != null)
{
this.ftdiPort.SetBreak(this.breakFlag);
}
else
{
this.serialPort1.BreakState = this.breakFlag;
}
}
}
///
/// Gets the detected W1 parameter.
///
public int w1 { get; private set; }
///
/// Gets the detected W2 parameter.
///
public int w2 { get; private set; }
///
/// Gets the detected W3 parameter.
///
public int w3 { get; private set; }
///
/// Gets the W4 parameter.
///
public int w4 { get; private set; }
///
/// Gets or sets baud rate for connection.
///
public int baudRate
{
get
{
return this.speed;
}
set
{
this.speed = value;
if (this.ftdiPort != null)
{
this.ftdiPort.SetBaudRate((uint)this.speed);
}
else
{
if (this.serialPort1 != null)
{
this.serialPort1.BaudRate = this.speed;
}
}
}
}
///
/// Gets or sets a value indicating whether the line status data changes shall be echoed.
///
public bool echoLine { get; set; }
///
/// Gets or sets a value indicating whether modem signal changes shall be echoed.
///
public bool echoModem { get; set; }
///
/// Selected FTDI port info data.
///
private FT_DEVICE_INFO_NODE selectedFtdiPort;
///
/// Initializes a new instance of the class.
///
/// FTDI access instance.
/// Com port name,
/// Selected FTDI port.
/// Communication speed.
/// Text logging interface instance.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "comPort", Justification = "Reviewed.")]
public SimpleSerialDevice(FTDI ftdi, string comPort, FT_DEVICE_INFO_NODE selectedFtdiPort, int speed, ILogging iTextLogging)
{
this.ftdi = ftdi;
this.comPort = comPort;
this.selectedFtdiPort = selectedFtdiPort;
this.speed = speed;
this.iTextLogging = iTextLogging;
this.echoLine = false;
this.echoModem = false;
this.calibrate = true;
this.lLine = false;
this.w1 = 0;
this.w2 = 0;
this.w3 = 0;
this.w4 = 25;
}
///
/// Get array of FTDI devices.
///
/// Array of FTDI devices.
public static FT_DEVICE_INFO_NODE[] ftdiDevices()
{
FT_DEVICE_INFO_NODE[] newDeviceNames = null;
List deviceNameList = new List();
FTDI ftdi = new FTDI();
try
{
uint devcount = 0;
ftdi.GetNumberOfDevices(ref devcount);
FT_DEVICE_INFO_NODE[] devicelist = new FT_DEVICE_INFO_NODE[devcount];
ftdi.GetDeviceList(devicelist);
foreach (FT_DEVICE_INFO_NODE deviceItem in devicelist)
{
if (deviceItem != null && !string.IsNullOrEmpty(deviceItem.Description))
{
deviceNameList.Add(deviceItem);
}
}
}
catch (FT_EXCEPTION ex)
{
MessageBox.Show(ex.GetType().ToString() + "\r\n" + ex.Message);
}
finally
{
newDeviceNames = deviceNameList.ToArray();
}
return newDeviceNames;
}
///
/// Initialize the communication using a background thread.
///
public void init()
{
this.initThread = new Thread(this.initThreadImpl);
this.initThread.Start();
}
///
/// Clean up resources.
///
public void Dispose()
{
this.close();
this.dataReceivedHandler.Dispose();
this.receivedDataEvent.Dispose();
}
///
/// Open a new connection.
///
public void open()
{
// if (this.comPort.StartsWith("FTDI:"))
if (this.selectedFtdiPort != null)
{
this.ftdiPort = new FtdiPort(this.ftdi);
this.ftdiValidData = true;
this.ftdiPort.OpenByLocation(this.selectedFtdiPort.LocId);
this.ftdiPort.SetBaudRate((uint)this.speed);
this.ftdiPort.SetDataCharacteristics(
FT_DATA_BITS.FT_BITS_8,
FT_STOP_BITS.FT_STOP_BITS_1,
FT_PARITY.FT_PARITY_NONE);
this.ftdiPort.SetLatency(2);
this.ftdiPort.SetFlowControl(FT_FLOW_CONTROL.FT_FLOW_NONE, 0, 0);
this.receivedDataEvent = new AutoResetEvent(false);
this.ftdiPort.SetEventNotification(
FT_EVENTS.FT_EVENT_RXCHAR | FT_EVENTS.FT_EVENT_LINE_STATUS | FT_EVENTS.FT_EVENT_MODEM_STATUS,
this.receivedDataEvent);
this.dataReceivedHandler = new BackgroundWorker();
this.dataReceivedHandler.DoWork += this.ReadData;
if (!this.dataReceivedHandler.IsBusy)
{
this.dataReceivedHandler.RunWorkerAsync();
}
}
else
{
this.serialPort1 = new SerialPort(this.comPort, this.speed, Parity.None, 8, StopBits.One);
this.serialPort1.Handshake = Handshake.None;
this.serialPort1.Open();
this.serialPort1.ErrorReceived += new System.IO.Ports.SerialErrorReceivedEventHandler(this.serialPort1_ErrorReceived);
this.serialPort1.PinChanged += new System.IO.Ports.SerialPinChangedEventHandler(this.serialPort1_PinChanged);
this.serialDataReceivedEventHandler = new System.IO.Ports.SerialDataReceivedEventHandler(this.serialPort1_DataReceived);
this.serialPort1.DataReceived += this.serialDataReceivedEventHandler;
this.iTextLogging.appendText("Encoding=" + this.serialPort1.Encoding.EncodingName + "\r\n");
}
}
///
/// Close the current connection.
///
public void close()
{
this.doRun = false;
this.connected = false;
if (this.initThread != null)
{
this.initThread.Abort();
this.initThread.Interrupt();
this.initThread.Join(4000);
}
if (this.ftdiPort != null)
{
try
{
this.ftdiPort.Close();
}
catch (Exception ex)
{
this.iTextLogging.appendText("ftdi; Close failed: " + ex.Message + "\r\n");
}
finally
{
this.ftdiPort = null;
}
}
if (this.serialPort1 != null)
{
try
{
this.serialPort1.Close();
}
catch (Exception ex)
{
this.iTextLogging.appendText("serialPort1; Close failed: " + ex.Message + "\r\n");
}
finally
{
this.serialPort1 = null;
}
}
}
///
/// Write data to device.
///
/// Data to write.
public void write(byte[] ba0)
{
if (ba0 != null)
{
lock (this.lockObject2)
{
foreach (byte b0 in ba0)
{
byte[] ba = new byte[] { b0 };
if (this.ftdiPort != null)
{
uint written = 0;
this.ftdiPort.Write(ba, ba.Length, ref written);
if (written != ba.Length)
{
this.iTextLogging.appendText("\r\nWritten less than provided! (written=" + written + ", requested=" + ba.Length + ")\r\n");
}
}
else
{
this.serialPort1.Write(ba, 0, ba.Length);
}
Thread.Sleep(10);
}
}
}
}
///
/// Build initialization packet.
///
/// Initialization packet data.
private static uint buildInitPacket()
{
byte startByte = 0x0;
byte stopByte = 0xff;
byte contentByte = 0x33;
uint dataPacket = (uint)(stopByte & 0x01);
dataPacket = (uint)(dataPacket << 8) | (uint)(contentByte & 0xff);
dataPacket = (uint)(dataPacket << 1) | (uint)(startByte & 0x01);
return dataPacket;
}
///
/// Perform port event tasks.
///
private void portEvent()
{
if (this.ftdiPort != null)
{
this.handleModemStatus();
this.handleLineStatus();
this.readData();
}
}
///
/// Read data from device.
///
/// Sending item.
/// Event arguments.
private void ReadData(object pSender, DoWorkEventArgs pEventArgs)
{
try
{
while (true)
{
// wait until event is fired
this.receivedDataEvent.WaitOne();
this.portEvent();
}
}
catch (Exception ex)
{
this.iTextLogging.appendText("ReadData failed: " + ex.Message + "\r\n" + ex.StackTrace + "\r\n");
}
}
///
/// Read data.
///
/// Data read.
private uint readData()
{
uint nrOfBytesAvailable = 0;
// try to recieve data now
this.ftdiPort.GetRxBytesAvailable(ref nrOfBytesAvailable);
// If data exists and is valid.
if (this.ftdiValidData && nrOfBytesAvailable > 0)
{
byte[] readData = new byte[nrOfBytesAvailable];
uint numBytesRead = 0;
this.ftdiPort.Read(readData, nrOfBytesAvailable, ref numBytesRead);
long t1 = DateTime.Now.Ticks;
this.delta = (t1 - this.lastReadTs) / (TimeSpan.TicksPerSecond / 1000);
if (this.delta > 200)
{
this.iTextLogging.appendText("\r\n");
}
this.iTextLogging.appendText(" [" + this.delta + "]");
this.lastReadTs = t1;
this.iTextLogging.appendText(" {" + numBytesRead + "}");
for (int i = 0; i < numBytesRead && i < nrOfBytesAvailable; i++)
{
this.handleChar(readData[i]);
}
}
return nrOfBytesAvailable;
}
///
/// Handle changes in modem status lines.
///
private void handleModemStatus()
{
if (this.echoLine)
{
byte modemStatus = 0;
this.ftdiPort.GetModemStatus(ref modemStatus);
if (modemStatus != this.oldModemStatus)
{
if ((FT_MODEM_STATUS.FT_CTS & modemStatus) != (FT_MODEM_STATUS.FT_CTS & this.oldModemStatus))
{
if ((FT_MODEM_STATUS.FT_CTS & modemStatus) != 0)
{
this.iTextLogging.appendText("◅CTS+▻");
}
else
{
this.iTextLogging.appendText("◅CTS-▻");
}
}
if ((FT_MODEM_STATUS.FT_DCD & modemStatus) != (FT_MODEM_STATUS.FT_DCD & this.oldModemStatus))
{
if ((FT_MODEM_STATUS.FT_DCD & modemStatus) != 0)
{
this.iTextLogging.appendText("◅DCD+▻");
}
else
{
this.iTextLogging.appendText("◅DCD-▻");
}
}
if ((FT_MODEM_STATUS.FT_DSR & modemStatus) != (FT_MODEM_STATUS.FT_DSR & this.oldModemStatus))
{
if ((FT_MODEM_STATUS.FT_DSR & modemStatus) != 0)
{
this.iTextLogging.appendText("◅DSR+▻");
}
else
{
this.iTextLogging.appendText("◅DSR-▻");
}
}
if ((FT_MODEM_STATUS.FT_RI & modemStatus) != (FT_MODEM_STATUS.FT_RI & this.oldModemStatus))
{
if ((FT_MODEM_STATUS.FT_RI & modemStatus) != 0)
{
this.iTextLogging.appendText("◅RI+▻");
}
else
{
this.iTextLogging.appendText("◅RI-▻");
}
}
this.oldModemStatus = modemStatus;
}
}
}
///
/// Handle data line information/errors.
///
private void handleLineStatus()
{
if (this.echoModem)
{
byte lineStatus = 0;
this.ftdiPort.GetLineStatus(ref lineStatus);
if (lineStatus > 0)
{
if ((FT_LINE_STATUS.FT_BI & lineStatus) != 0)
{
this.iTextLogging.appendText("⌦BREAK⌫");
}
if ((FT_LINE_STATUS.FT_FE & lineStatus) != 0)
{
this.iTextLogging.appendText("⌦Framing Error⌫");
}
if ((FT_LINE_STATUS.FT_OE & lineStatus) != 0)
{
this.iTextLogging.appendText("⌦Overrun Error⌫");
}
if ((FT_LINE_STATUS.FT_PE & lineStatus) != 0)
{
this.iTextLogging.appendText("⌦Parity Error⌫");
}
}
}
}
///
/// Read characters.
///
private void readChars()
{
while (this.serialPort1.BytesToRead > 0)
{
this.readChar();
}
}
///
/// Read single character.
///
private void readChar()
{
int ch = this.serialPort1.ReadChar();
this.handleChar(ch);
}
///
/// Handle single character.
///
/// Character to handle.
private void handleChar(int ch)
{
if (this.connected)
{
if (this.discardChars > 0)
{
this.discardChars--;
}
else
{
this.iTextLogging.appendText(" " + ch.ToString("x2"));
}
}
else
{
if (ch == 0x55 && this.wIndex == 0)
{
this.w1 = (int)this.delta;
this.wIndex++;
this.iTextLogging.appendText("!");
}
this.iTextLogging.appendText(" " + ch.ToString("x2"));
if (this.expectByteSet != null)
{
if (this.rxBytes == null)
{
foreach (byte[] expectBytes1 in this.expectByteSet)
{
this.expectByteLength = (this.expectByteLength < expectBytes1.Length) ? expectBytes1.Length : this.expectByteLength;
}
this.rxBytes = new byte[this.expectByteLength];
this.rxIx = 0;
}
bool match = false;
foreach (byte[] expectBytes1 in this.expectByteSet)
{
if (this.rxIx < expectBytes1.Length && expectBytes1[this.rxIx] == (byte)ch)
{
match = true;
this.rxBytes[this.rxIx] = (byte)ch;
break;
}
}
if (match && this.expectStage == 1)
{
if (this.wIndex == 1 && this.rxIx == 0)
{
this.w2 = (int)this.delta;
}
if (this.wIndex == 1 && this.rxIx == 1)
{
this.w3 = (int)this.delta;
this.wIndex += 2;
Thread.Sleep(this.w4);
this.ackKeyBytes(this.rxBytes[this.rxIx]);
this.expectStage = 2;
}
this.rxIx++;
}
else
{
if (match && this.expectStage == 2)
{
this.rxIx++;
}
else
{
this.rxIx = 0;
}
}
if (this.rxIx == this.expectByteLength)
{
lock (this.lockObject)
{
Monitor.PulseAll(this.lockObject);
}
}
}
}
}
///
/// Initialization thread implementation.
///
private void initThreadImpl()
{
this.doRun = true;
try
{
this.iTextLogging.appendText("5 baud init.\r\n");
this.calibrateDelay();
uint dataPacket = buildInitPacket();
this.rxBytes = null;
this.rxIx = 0;
this.expectByteSet = EXPECT_BYTE_PAIRS;
this.expectByteLength = 0;
this.expectStage = 1;
this.wIndex = 0;
this.sendInitPacket(dataPacket);
this.iTextLogging.appendText("\r\nExpect data...\r\n");
lock (this.lockObject)
{
Monitor.Wait(this.lockObject, 4000);
}
if (this.rxBytes == null)
{
this.iTextLogging.appendText("\r\nNo data 1...\r\n");
return;
}
int keyWordIndex;
bool match;
byte keyByte2;
this.verifyResponse(out keyWordIndex, out match, out keyByte2);
// Clear and set next expected data.
this.rxBytes = null;
this.rxIx = 0;
this.expectByteSet = EXPECT_BYTE2;
this.expectByteLength = 0;
if (!match)
{
this.iTextLogging.appendText("\r\nNo Match 1! - Exiting!\r\n");
return;
}
// Write log event here to minimize timing error impact.
this.iTextLogging.appendText("\r\nMatch 1!\r\n");
// Wait for second expected data (0xcc).
lock (this.lockObject)
{
Monitor.Wait(this.lockObject, 4000);
}
if (this.rxBytes == null)
{
this.iTextLogging.appendText("\r\nNo data 2...\r\n");
return;
}
int ix = 0;
this.verifyResponse(out ix, out match, out keyByte2);
if (!match)
{
this.iTextLogging.appendText("\r\nNo Match 2! - Exiting!\r\n");
return;
}
this.iTextLogging.appendText("\r\nMatch 2! keyWordIndex=" + keyWordIndex + "\r\n");
this.displayInfo(keyWordIndex);
byte[] keepAliveData;
if (keyWordIndex <= 1)
{
keepAliveData = KEEPALIVE_REQ_1;
}
else
{
keepAliveData = KEEPALIVE_REQ_2;
}
this.connected = true;
Thread.Sleep(100);
while (this.doRun)
{
this.discardChars = keepAliveData.Length;
this.write(keepAliveData);
Thread.Sleep(3000);
}
}
catch (InvalidOperationException)
{
// Ignore.
}
}
///
/// Display protocol information.
///
/// Protocol index value.
private void displayInfo(int ix)
{
switch (ix)
{
case 0:
this.iTextLogging.appendText("Protocol ISO9141 [0x0808]\r\n");
break;
case 1:
this.iTextLogging.appendText("Protocol ISO9141 [0x9494]\r\n");
break;
case 2:
this.iTextLogging.appendText("Protocol ISO14230 [0x8fe9]\r\n");
break;
case 3:
this.iTextLogging.appendText("Protocol ISO14230 [0x8f6b]\r\n");
break;
case 4:
this.iTextLogging.appendText("Protocol ISO14230 [0x8f6d]\r\n");
break;
case 5:
this.iTextLogging.appendText("Protocol ISO14230 [0x8fef]\r\n");
break;
}
this.iTextLogging.appendText("W1=" + this.w1 + ", W2=" + this.w2 + ", W3=" + this.w3 + ", W4=" + this.w4 + "\r\n");
}
///
/// Acknowledge key byte.
///
/// Key byte to acknowledge.
private void ackKeyBytes(byte keyByte2)
{
byte[] buf = new byte[1];
buf[0] = (byte)(~keyByte2 & 0xff);
this.iTextLogging.appendText("");
this.write(buf);
}
///
/// Verify response data against expected data.
///
/// Index value for matching data.
/// Flag indicating if there was a match.
/// Key byte value.
private void verifyResponse(out int ix, out bool match, out byte keyByte2)
{
ix = 0;
keyByte2 = 0x00;
match = false;
foreach (byte[] expectBytes1 in this.expectByteSet)
{
string s2 = string.Empty;
for (int i = 0; i < expectBytes1.Length; i++)
{
s2 += ", 0x" + expectBytes1[i].ToString("x2");
}
match = true;
for (int i = 0; i < this.rxBytes.Length && i < expectBytes1.Length; i++)
{
if (expectBytes1[i] != this.rxBytes[i])
{
match = false;
break;
}
}
if (match)
{
if (this.rxBytes.Length >= 2)
{
keyByte2 = this.rxBytes[1];
}
break;
}
ix++;
}
this.iTextLogging.appendText("ix=" + ix + "\r\n");
}
///
/// Send initialization packet.
///
/// Data packet to send.
private void sendInitPacket(uint dataPacket)
{
if (this.ftdiPort != null)
{
this.ftdiValidData = false;
}
else
{
this.serialPort1.DataReceived -= this.serialDataReceivedEventHandler;
}
if (this.ftdiPort != null)
{
this.ftdiPort.Purge(FT_PURGE.FT_PURGE_RX);
}
else
{
this.serialPort1.DiscardInBuffer();
}
this.txData(10, this.newDelay, dataPacket);
// Just in case.
this.breakState = false;
// To get correct time for W1
this.lastReadTs = DateTime.Now.Ticks;
if (this.ftdiPort != null)
{
this.ftdiValidData = true;
}
else
{
this.serialPort1.DataReceived += this.serialDataReceivedEventHandler;
}
}
///
/// Calibrate delay value to compensate for delays in the computer.
///
private void calibrateDelay()
{
if (this.calibrate)
{
this.iTextLogging.appendText("Calibrating for 5 baud tx.\r\n");
this.newDelay = this.calibrateFiveBaud();
if (this.newDelay < 0)
{
this.iTextLogging.appendText("Error! Negative delay calculated!\r\n");
throw new Exception("Negative delay calculated!");
}
}
this.iTextLogging.appendText("\r\nnewDelay=" + this.newDelay + "\r\n");
}
///
/// Calibrate the delay for correct transmission of five baud data.
///
/// Calibrated delay value.
private int calibrateFiveBaud()
{
int bits = 32;
int delay = 200;
long t0 = 0;
long t1 = 0;
this.txData(bits, delay, 0xff0000ff, out t0, out t1);
long ticksPerBit = (t1 - t0) / bits;
long timePerBit = (ticksPerBit / 10000);
int newDelay1 = (int)(delay - (timePerBit - delay));
this.iTextLogging.appendText("Calibrated!\r\n");
// Just in case.
this.breakState = false;
Thread.Sleep(300);
return newDelay1;
}
///
/// Send 5 baud data.
///
/// Bits to send.
/// Delay value.
/// Data packet to send.
private void txData(int bits, int delay, uint dataPacket)
{
long t0;
long t1;
this.txData(bits, delay, dataPacket, out t0, out t1);
}
///
/// Send 5 baud data.
///
/// Bits to send.
/// Delay value.
/// Data packet to send.
/// Start time.
/// End time.
private void txData(int bits, int delay, uint dataPacket, out long t0, out long t1)
{
ThreadPriority currentPrio = Thread.CurrentThread.Priority;
try
{
Thread.CurrentThread.Priority = ThreadPriority.Highest;
t0 = DateTime.Now.Ticks;
for (int i = 0; i < bits; i++)
{
if ((dataPacket & (1 << i)) != 0)
{
this.setLineState(false);
this.iTextLogging.appendText("1");
}
else
{
this.setLineState(true);
this.iTextLogging.appendText("0");
}
Thread.Sleep(delay);
}
t1 = DateTime.Now.Ticks;
}
finally
{
Thread.CurrentThread.Priority = currentPrio;
}
}
///
/// Set signal line status value.
///
/// State for signal.
private void setLineState(bool state)
{
if (this.lLine)
{
this.dtrState = state;
}
this.breakState = state;
}
///
/// Handle error event from serial port.
///
/// Sending object.
/// Event Data.
private void serialPort1_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
{
if (this.serialPort1 != null)
{
SerialError err = e.EventType;
this.iTextLogging.appendText("Error Received :" + this.serialPort1.PortName + ", error=" + err + "\r\n");
}
}
///
/// Handle Data Received event.
///
/// Sending object.
/// Event Data.
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (this.serialPort1 != null)
{
SerialData serialData = e.EventType;
this.readChars();
}
}
///
/// Handle pin changed event.
///
/// Sending object.
/// Event Data.
private void serialPort1_PinChanged(object sender, SerialPinChangedEventArgs e)
{
/*
if (this.serialPort1 != null)
{
SerialPinChange spc = e.EventType;
}
*/
}
}
}