//-----------------------------------------------------------------------
//
// 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 LoggingLibrary
{
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;
using System.Windows.Forms;
using global::SharedObjects;
using global::SharedObjects.Api;
using global::SharedObjects.CAN.Objects;
using global::SharedObjects.Misc;
///
/// Class for logging of events.
///
public partial class LoggingForm : Form, ILogging
{
///
/// Indicator of no file.
///
public const string NOFILE = " * None * ";
///
/// Light red color.
///
private static readonly Color color1 = Color.FromArgb(0xff, 0xA0, 0xA0);
///
/// Light yellow color.
///
private static readonly Color color2 = Color.FromArgb(0xff, 0xff, 0xA0);
///
/// Light green color.
///
private static readonly Color color3 = Color.FromArgb(0xA0, 0xff, 0xA0);
///
/// Light cyan color.
///
private static readonly Color color4 = Color.FromArgb(0xA0, 0xff, 0xff);
///
/// Light blue color.
///
private static readonly Color color5 = Color.FromArgb(0xA0, 0xA0, 0xff);
///
/// Light magenta color.
///
private static readonly Color color6 = Color.FromArgb(0xff, 0xA0, 0xff);
///
/// Light gray color.
///
private static readonly Color color7 = Color.LightGray;
///
/// Current RAW data log stream.
///
private FileStream rawDataLogStream = null;
///
/// Current RAW writer.
///
private BinaryWriter rawBinaryWriter = null;
///
/// Current CAN data log stream.
///
private FileStream canDataLogStream = null;
///
/// Current event log stream.
///
private FileStream eventLogStream = null;
///
/// Current CSV data log stream.
///
private FileStream valueDataLogStream = null;
///
/// Current CAN data log writer instance.
///
private TextWriter canDataLogWriter;
///
/// Current event log writer.
///
private TextWriter eventLogWriter = null;
///
/// Current CSV data log writer.
///
private TextWriter valueDataLogWriter;
///
/// Baseline timestamp for CAN log file.
///
private long t0 = 0;
///
/// To capture uncontrolled closing of the window.
///
private bool controlledClose = false;
///
/// Queue for displaying message items.
///
private Queue dispQueue = new Queue();
///
/// Object to synchronize threads with.
///
private object lockObject = new object();
///
/// Thread handling receiving of messages.
///
private Thread queueThread = null;
///
/// Flag indicate running thread.
///
private bool doRun = true;
///
/// Start time.
///
private long tBase = 0L;
///
/// Flag indicating that EOL (CR+LF) was received and that next line shall start with a timestamp.
///
private bool gotEol = true;
///
/// Rows in traffic data grid view.
///
private DataGridViewRowCollection trafficRows = null;
///
/// Initializes a new instance of the class.
///
public LoggingForm()
{
this.tBase = DateTime.Now.Ticks;
this.canDataLogWriter = null;
this.valueDataLogWriter = null;
this.InitializeComponent();
this.trafficRows = this.trafficDataGridView.Rows;
this.logLevelCB.SelectedIndex = 2;
this.queueThread = new Thread(new ThreadStart(this.displayQueueItems));
this.queueThread.Name = "Logging Queue Thread";
this.queueThread.Start();
}
///
/// Closing of the log form.
///
/// To capture uncontrolled closing of the window.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "controlledClose", Justification = "Reviewed, intentional.")]
public void Close(bool controlledClose)
{
this.doRun = false;
this.queueThread.Abort();
this.queueThread.Interrupt();
this.queueThread.Join(4000);
this.controlledClose = controlledClose;
this.Close();
}
///
/// Add one row to the traffic table.
///
/// Traffic direction and layer level.
/// Address field.
/// Mode field.
/// PID field.
/// Payload length.
/// Payload data.
public void addRow(rowMode direction, uint address, byte mode, uint pid, uint length, string rspStr)
{
if (this.trafficDataGridView.InvokeRequired)
{
try
{
this.trafficDataGridView.Invoke(new addRowFunc(this.addRow), new object[] { direction, address, mode, pid, length, rspStr });
}
catch (ThreadAbortException)
{
throw;
}
catch (System.Reflection.TargetParameterCountException ex)
{
MessageBox.Show(
"Exception: " + ex.Message + Utils.CRLF,
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
}
catch (System.ObjectDisposedException)
{
// Ignore.
}
}
else
{
// Only keep the 1000 last rows.
while (this.trafficRows.Count > 1000)
{
this.trafficRows.RemoveAt(0);
}
int n = this.trafficRows.Add();
DataGridViewRow row = this.trafficRows[n];
DataGridViewCellCollection cells = row.Cells;
string directionStr = string.Empty;
switch (direction)
{
case rowMode.transport_in:
directionStr = "In1";
row.DefaultCellStyle.BackColor = color1;
break;
case rowMode.session_in:
directionStr = "In2";
row.DefaultCellStyle.BackColor = color2;
break;
case rowMode.presentation_in:
directionStr = "In3";
row.DefaultCellStyle.BackColor = color3;
break;
case rowMode.presentation_out:
directionStr = "Out3";
row.DefaultCellStyle.BackColor = color4;
break;
case rowMode.session_out:
directionStr = "Out2";
row.DefaultCellStyle.BackColor = color5;
break;
case rowMode.transport_out:
directionStr = "Out1";
row.DefaultCellStyle.BackColor = color6;
break;
case rowMode.data_discard:
directionStr = "-";
row.DefaultCellStyle.BackColor = color7;
break;
}
cells["col_timestamp"].Value = ((DateTime.Now.Ticks - this.tBase) / (double)TimeSpan.TicksPerSecond).ToString("0.0000");
cells["col_direction"].Value = directionStr;
cells["col_address"].Value = "0x" + address.ToString("X4");
if (direction == rowMode.presentation_in || direction == rowMode.presentation_out)
{
cells["col_mode"].Value = "0x" + mode.ToString("X2");
cells["col_pid"].Value = "0x" + pid.ToString("X2");
cells["col_length"].Value = length.ToString();
}
cells["col_payload"].Value = rspStr;
this.trafficDataGridView.FirstDisplayedScrollingRowIndex = n > 10 ? n - 10 : n;
}
}
///
/// Open CAN data log.
///
/// 'true' if successful.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "Reviewed, intentional.")]
public bool openCanDataLog()
{
bool success = false;
this.saveFileDialog2.FileName = string.Empty;
this.saveFileDialog2.DefaultExt = ".asc";
this.saveFileDialog2.AddExtension = true;
this.saveFileDialog2.ValidateNames = true;
this.saveFileDialog2.Filter = "Vector Canalyzer Asc Format|*.asc";
this.saveFileDialog2.ShowDialog();
if (!string.IsNullOrEmpty(this.saveFileDialog2.FileName))
{
try
{
this.canDataLogStream = (System.IO.FileStream)this.saveFileDialog2.OpenFile();
this.canDataLogWriter = new StreamWriter(this.canDataLogStream, Utils.iso_8859_1);
DateTime thisDate1 = DateTime.Now;
string dateTimeString = thisDate1.ToString("ddd MMM dd hh:mm:ss ", CultureInfo.CreateSpecificCulture("en-US"))
+ thisDate1.ToString("tt", CultureInfo.CreateSpecificCulture("en-US")).ToLower(CultureInfo.InvariantCulture)
+ thisDate1.ToString(" yyyy", CultureInfo.CreateSpecificCulture("en-US"));
this.canDataLogWriter.WriteLine("date " + dateTimeString);
this.canDataLogWriter.WriteLine("base hex timestamps absolute");
this.canDataLogWriter.WriteLine("internal events logged");
this.canDataLogWriter.WriteLine("// version 7.6.0");
this.canDataLogWriter.WriteLine("Begin Triggerblock " + dateTimeString);
this.t0 = thisDate1.Ticks;
long t1 = this.t0;
string secStr = getSecondsStr(this.t0, t1);
this.canDataLogWriter.WriteLine(secStr + " Start of measurement");
this.canDataLogFileName.Text = this.saveFileDialog2.FileName;
success = true;
}
catch (Exception ex)
{
MessageBox.Show(
ex.Message,
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
}
}
return success;
}
///
/// Get seconds string.
///
/// The number of seconds is relative to a saved timestamp.
///
///
/// Timestamp to relate to.
/// Seconds string.
public string getSecondsStr(long t1)
{
return getSecondsStr(this.t0, t1);
}
///
/// Add raw log data to binary log file.
///
/// Binary object to write to file.
public void addRawData(RawLogObject rawLogObject)
{
if (rawLogObject != null && this.rawDataLogStream != null && this.rawBinaryWriter != null)
{
this.rawBinaryWriter.Write(rawLogObject.timestamp);
this.rawBinaryWriter.Write(rawLogObject.sourceAddress);
this.rawBinaryWriter.Write(rawLogObject.mode);
this.rawBinaryWriter.Write(rawLogObject.pid_int);
this.rawBinaryWriter.Write(rawLogObject.dataLen);
this.rawBinaryWriter.Write(rawLogObject.data);
this.rawBinaryWriter.Flush();
this.rawDataLogStream.Flush();
}
}
///
/// Open event log.
///
/// 'true' if successful.
public bool openRawLog()
{
this.closeRawLog();
bool success = false;
this.saveFileDialog4.FileName = string.Empty;
this.saveFileDialog4.DefaultExt = ".raw";
this.saveFileDialog4.AddExtension = true;
this.saveFileDialog4.ValidateNames = true;
this.saveFileDialog4.Filter = "Raw File|*.raw";
if (this.saveFileDialog4.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
if (!string.IsNullOrEmpty(this.saveFileDialog4.FileName))
{
try
{
this.rawDataLogStream = (System.IO.FileStream)this.saveFileDialog4.OpenFile();
this.rawBinaryWriter = new BinaryWriter(this.rawDataLogStream);
success = true;
}
catch (Exception ex)
{
MessageBox.Show(
ex.Message,
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
}
}
}
return success;
}
///
/// Close event log.
///
public void closeRawLog()
{
if (this.rawBinaryWriter != null)
{
try
{
this.rawBinaryWriter.Flush();
}
catch
{
}
}
if (this.rawDataLogStream != null)
{
try
{
this.rawDataLogStream.Flush();
}
catch
{
}
}
if (this.rawBinaryWriter != null)
{
try
{
this.rawBinaryWriter.Close();
}
catch
{
}
this.rawBinaryWriter = null;
}
if (this.rawDataLogStream != null)
{
try
{
this.rawDataLogStream.Close();
}
catch
{
}
this.rawDataLogStream = null;
}
}
///
/// Open event log.
///
/// 'true' if successful.
public bool openEventLog()
{
bool success = false;
this.saveFileDialog1.FileName = string.Empty;
this.saveFileDialog1.DefaultExt = ".txt";
this.saveFileDialog1.AddExtension = true;
this.saveFileDialog1.ValidateNames = true;
this.saveFileDialog1.Filter = "Text File|*.txt";
this.saveFileDialog1.ShowDialog();
if (!string.IsNullOrEmpty(this.saveFileDialog1.FileName))
{
try
{
this.eventLogStream = (System.IO.FileStream)this.saveFileDialog1.OpenFile();
this.eventLogWriter = new StreamWriter(this.eventLogStream);
this.eventLogFileName.Text = this.saveFileDialog1.FileName;
success = true;
}
catch (Exception ex)
{
MessageBox.Show(
ex.Message,
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
}
}
return success;
}
///
/// Close event log.
///
public void closeEventLog()
{
if (this.eventLogStream != null)
{
try
{
this.eventLogStream.Flush();
}
catch
{
}
}
if (this.eventLogWriter != null)
{
try
{
this.eventLogWriter.Close();
}
catch
{
}
this.eventLogWriter = null;
}
if (this.eventLogStream != null)
{
try
{
this.eventLogStream.Close();
}
catch
{
}
this.eventLogStream = null;
}
this.eventLogFileName.Text = NOFILE;
}
///
/// Save event window.
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "Reviewed.")]
public void saveEventWindow()
{
this.saveFileDialog1.FileName = string.Empty;
this.saveFileDialog1.DefaultExt = ".txt";
this.saveFileDialog1.AddExtension = true;
this.saveFileDialog1.ValidateNames = true;
this.saveFileDialog1.Filter = "Text File|*.txt";
this.saveFileDialog1.ShowDialog();
if (!string.IsNullOrEmpty(this.saveFileDialog1.FileName))
{
try
{
using (FileStream fs = (System.IO.FileStream)this.saveFileDialog1.OpenFile())
{
try
{
using (StreamWriter sw = new StreamWriter(fs))
{
try
{
sw.WriteLine(this.eventLogWindow.Text);
}
finally
{
try
{
sw.Close();
}
catch
{
}
}
}
}
finally
{
try
{
fs.Close();
}
catch
{
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(
ex.Message + "\r\n" + ex.StackTrace,
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
}
}
}
///
/// Only append to CAN data log file, not window.
///
/// Text string to append.
public void appendLog(string txt)
{
if (this.eventLogWriter != null)
{
this.eventLogWriter.Write(txt);
if (this.eventLogWriter != null)
{
try
{
this.eventLogWriter.Flush();
}
catch
{
}
}
}
}
///
/// Open measurements data log file.
///
/// 'true' if successful.
public bool openDataLog()
{
bool success = false;
this.saveFileDialog3.FileName = string.Empty;
this.saveFileDialog3.DefaultExt = ".csv";
this.saveFileDialog3.AddExtension = true;
this.saveFileDialog3.ValidateNames = true;
this.saveFileDialog3.Filter = "Excel CSV Format|*.csv";
DialogResult res = this.saveFileDialog3.ShowDialog();
if (res == DialogResult.OK)
{
if (!string.IsNullOrEmpty(this.saveFileDialog3.FileName))
{
try
{
this.valueDataLogStream = (System.IO.FileStream)this.saveFileDialog3.OpenFile();
this.valueDataLogWriter = new StreamWriter(this.valueDataLogStream, Utils.iso_8859_1);
this.measurementDataLogFileName.Text = this.saveFileDialog3.FileName;
success = true;
}
catch (Exception ex)
{
MessageBox.Show(
ex.Message,
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
}
}
}
return success;
}
///
/// Close measurements data log file.
///
public void closeDataLog()
{
if (this.valueDataLogWriter != null)
{
try
{
this.valueDataLogWriter.Flush();
}
catch
{
}
}
if (this.valueDataLogStream != null)
{
try
{
this.valueDataLogStream.Flush();
}
catch
{
}
}
if (this.valueDataLogWriter != null)
{
try
{
this.valueDataLogWriter.Close();
}
catch
{
}
this.valueDataLogWriter = null;
}
if (this.valueDataLogStream != null)
{
try
{
this.valueDataLogStream.Close();
}
catch
{
}
this.valueDataLogStream = null;
}
this.measurementDataLogFileName.Text = NOFILE;
}
///
/// Close CAN data log.
///
public void closeCanDataLog()
{
if (this.canDataLogWriter != null)
{
try
{
this.canDataLogWriter.WriteLine("End TriggerBlock");
}
catch
{
}
try
{
this.canDataLogWriter.Flush();
}
catch
{
}
}
if (this.canDataLogStream != null)
{
try
{
this.canDataLogStream.Flush();
}
catch
{
}
}
if (this.canDataLogWriter != null)
{
try
{
this.canDataLogWriter.Close();
}
catch
{
}
this.canDataLogWriter = null;
}
if (this.canDataLogStream != null)
{
try
{
this.canDataLogStream.Close();
}
catch
{
}
this.canDataLogStream = null;
}
this.canDataLogFileName.Text = NOFILE;
}
///
/// Append text to the text box.
///
/// Text to append, caller supplies line breaks.
public void appendText(string txt)
{
this.appendText(txt, LogLevel.LOG_INFO);
}
///
/// Append text to the text box.
///
public void appendTextLn()
{
this.appendText("\r\n", LogLevel.LOG_INFO);
}
///
/// Append text to the text box.
///
/// Text to append, caller supplies line breaks.
public void appendTextLn(string txt)
{
this.appendText(txt + "\r\n", LogLevel.LOG_INFO);
}
///
/// Append text to the text box.
/// The return value can be used to control a group of log messages
/// where the first only needs to be checked if no logging is needed.
/// This will improve performance.
///
/// Text to append, caller supplies line breaks.
/// Level of message, LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR
/// 'true' if message was logged at the given log level.
public bool appendText(string txt, int logLevel)
{
bool logged = false;
if (this.InvokeRequired)
{
try
{
logged = (bool)this.Invoke(new appendTextFunc(this.appendText), new object[] { txt, logLevel });
}
catch (System.Reflection.TargetParameterCountException ex)
{
MessageBox.Show(
"Exception: " + ex.Message + "\r\ntxt='" + txt + "'",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
}
catch (System.ObjectDisposedException)
{
// Ignore.
}
}
else
{
if (this.logLevelCB == null || logLevel > this.logLevelCB.SelectedIndex)
{
logged = true;
try
{
if (this.eventLogWindow.Text.Length > 10240)
{
this.eventLogWindow.Text = this.eventLogWindow.Text.Substring(this.eventLogWindow.Text.Length - 10240, 10240);
}
if (this.gotEol)
{
double tDiff = (DateTime.Now.Ticks - this.tBase) / (double)TimeSpan.TicksPerSecond;
txt = tDiff.ToString("000,000.000") + ": " + txt;
}
this.eventLogWindow.AppendText(txt);
this.gotEol = txt.EndsWith("\r\n");
if (this.eventLogWriter != null)
{
this.eventLogWriter.Write(txt);
if (this.eventLogWriter != null)
{
try
{
this.eventLogWriter.Flush();
}
catch
{
}
}
}
}
catch
{
}
}
}
return logged;
}
///
/// Test Device for error.
///
/// Involved device.
/// Status code to test.
/// 'true' if OK to continue.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Reviewed.")]
public bool errTestDevice(IPassThruDevice passThruDevice, PassThruConstants.resultCode res)
{
return this.errTest(res, passThruDevice.lastError);
}
///
/// Display message content.
///
/// Prefix to display with message, e.g. 'Tx'.
/// Message to display.
public void dispMsg(string prefix, IPassThruMsg msg)
{
string addr = string.Empty;
string data = string.Empty;
if (msg != null)
{
for (int i = 0; i < msg.DataSize; i++)
{
if (i < 4)
{
addr += msg.Data[i].ToString("x2") + " ";
}
else
{
data += msg.Data[i].ToString("x2") + " ";
}
}
this.dispMsg(prefix, msg, addr, data);
}
}
///
/// Display one raw message with header info.
///
/// Prefix to display with entry, e.g. 'Tx'
/// Message to display.
/// Address string.
/// Payload string.
public void dispMsg(string prefix, IPassThruMsg msg, string addressStr, string data)
{
lock (this.lockObject)
{
this.dispQueue.Enqueue(new DispQueueItem(prefix, msg, addressStr, data));
Monitor.PulseAll(this.lockObject);
}
}
///
/// Test Device for error.
///
/// Involved device.
/// Status code to test.
/// Error text.
/// 'true' if OK to continue.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Reviewed.")]
public bool errTestDevice(IPassThruDevice passThruDevice, PassThruConstants.resultCode res, out string errtext)
{
return this.errTest(res, passThruDevice.lastError, out errtext);
}
///
/// Test Connection for error.
///
/// Current connection.
/// Status code to test.
/// 'true' if OK to continue.
public bool errTestConnection(IPassThruConnection passThruConnection, PassThruConstants.resultCode res)
{
if (passThruConnection != null)
{
return this.errTest(res, passThruConnection.lastError);
}
return false;
}
///
/// Test Connection for error.
///
/// Current connection.
/// Status code to test.
/// Error text.
/// 'true' if OK to continue.
public bool errTestConnection(IPassThruConnection passThruConnection, PassThruConstants.resultCode res, out string errtext)
{
if (passThruConnection != null)
{
return this.errTest(res, passThruConnection.lastError, out errtext);
}
errtext = "passThruConnection is 'null'";
return false;
}
///
/// Test the result code for error and log.
///
/// Result code to test.
/// Last error string.
/// 'true' if OK to continue.
public bool errTest(PassThruConstants.resultCode res, string lastErrorStr)
{
string errtext;
return this.errTest(res, lastErrorStr, out errtext);
}
///
/// Test the result code for error and log.
///
/// Result code to test.
/// Last error string.
/// Error text returned to be used in a pop-up or similar.
/// 'true' if execution can continue.
public bool errTest(PassThruConstants.resultCode res, string lastErrorStr, out string errtext)
{
bool cont = true;
errtext = string.Empty;
if (res != 0)
{
cont = false;
switch (res)
{
case PassThruConstants.resultCode.ERR_SUCCESS:
break;
case PassThruConstants.resultCode.ERR_TIMEOUT:
errtext = "ERR_TIMEOUT";
break;
case PassThruConstants.resultCode.ERR_BUFFER_EMPTY:
errtext = "ERR_BUFFER_EMPTY";
break;
case PassThruConstants.resultCode.ERR_NOT_SUPPORTED:
case PassThruConstants.resultCode.ERR_INVALID_CHANNEL_ID:
case PassThruConstants.resultCode.ERR_INVALID_PROTOCOL_ID:
case PassThruConstants.resultCode.ERR_NULL_PARAMETER:
case PassThruConstants.resultCode.ERR_INVALID_IOCTL_VALUE:
case PassThruConstants.resultCode.ERR_INVALID_FLAGS:
case PassThruConstants.resultCode.ERR_FAILED:
case PassThruConstants.resultCode.ERR_DEVICE_NOT_CONNECTED:
case PassThruConstants.resultCode.ERR_INVALID_MSG:
case PassThruConstants.resultCode.ERR_INVALID_TIME_INTERVAL:
case PassThruConstants.resultCode.ERR_EXCEEDED_LIMIT:
case PassThruConstants.resultCode.ERR_INVALID_MSG_ID:
case PassThruConstants.resultCode.ERR_DEVICE_IN_USE:
case PassThruConstants.resultCode.ERR_INVALID_IOCTL_ID:
case PassThruConstants.resultCode.ERR_BUFFER_FULL:
case PassThruConstants.resultCode.ERR_BUFFER_OVERFLOW:
case PassThruConstants.resultCode.ERR_PIN_INVALID:
case PassThruConstants.resultCode.ERR_CHANNEL_IN_USE:
case PassThruConstants.resultCode.ERR_MSG_PROTOCOL_ID:
case PassThruConstants.resultCode.ERR_INVALID_FILTER_ID:
case PassThruConstants.resultCode.ERR_NO_FLOW_CONTROL:
case PassThruConstants.resultCode.ERR_NOT_UNIQUE:
case PassThruConstants.resultCode.ERR_INVALID_BAUDRATE:
case PassThruConstants.resultCode.ERR_INVALID_DEVICE_ID:
errtext = "[" + string.Format("0x{0:x}", res) + "] "
+ J2534_Error.errors[res].name + ": "
+ J2534_Error.errors[res].text + "\r\n"
+ " LastError: " + lastErrorStr;
this.appendText("E1: " + errtext + "\r\n");
break;
case PassThruConstants.resultCode.READ_FAIL:
errtext = "CanApp: Read Failure.";
this.appendText("E2: " + errtext + "\r\n");
break;
case PassThruConstants.resultCode.NO_CHANNEL:
errtext = "CanApp: Not Connected - please connect first.";
this.appendText("E3: " + errtext + "\r\n");
break;
default:
this.appendText("E4: [" + res + "] " + lastErrorStr + "\r\n");
break;
}
}
return cont;
}
///
/// This is where CSV file format data shall be written.
/// The contents depends on what the user selects.
///
/// Text writer instance.
public TextWriter getValueDataLogWriter()
{
return this.valueDataLogWriter;
}
///
/// Get the data log writer.
/// This is where ASC file format data shall be written.
/// The content is the raw CAN bus data.
///
/// Text writer instance.
public TextWriter getCanDataLogWriter()
{
return this.canDataLogWriter;
}
///
/// Get seconds string.
///
/// The number of seconds is the difference between the timestamps; t1 - t0.
///
///
/// First timestamp.
/// Second timestamp.
/// Seconds string.
private static string getSecondsStr(long t0, long t1)
{
double seconds = (t1 - t0) / (double)TimeSpan.TicksPerSecond;
string secStr = seconds.ToString("F6", CultureInfo.CreateSpecificCulture("en-US"));
if (secStr.Length < 11)
{
secStr = secStr.PadLeft(11, ' ');
}
return secStr;
}
///
/// Thread used to avoid deadlocks.
///
private void displayQueueItems()
{
while (this.doRun)
{
lock (this.lockObject)
{
if (this.dispQueue.Count == 0)
{
// Wait for data to enter or timeout (in case we god out of sync)
if (!Monitor.Wait(this.lockObject, 10000))
{
if (this.dispQueue.Count > 0)
{
this.appendTextLn("LoggingForm.displayQueueItems(): Timeout with data present. this.sendQueue.Count=" + this.dispQueue.Count);
}
}
}
}
DispQueueItem dispQueueItem = null;
lock (this.lockObject)
{
if (this.dispQueue.Count > 0)
{
dispQueueItem = this.dispQueue.Dequeue();
}
}
if (dispQueueItem != null)
{
this.dispMsgInt(dispQueueItem.prefix, dispQueueItem.msg, dispQueueItem.addressStr, dispQueueItem.data);
}
}
}
///
/// Display one raw message with header info.
///
/// Prefix to display with entry, e.g. 'Tx'
/// Message to display.
/// Address string.
/// Payload string.
private void dispMsgInt(string prefix, IPassThruMsg msg, string addressStr, string data)
{
if (this.InvokeRequired)
{
try
{
this.Invoke(new dispMsgIntFunc(this.dispMsgInt), new object[] { prefix, msg, addressStr, data });
}
catch (System.Threading.ThreadAbortException)
{
throw;
}
catch (System.Reflection.TargetParameterCountException ex)
{
MessageBox.Show(
"Exception: " + ex.Message + "\r\n",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
}
catch (System.ObjectDisposedException)
{
// Ignore.
}
}
else
{
if (msg != null)
{
this.appendText(prefix + " [" + msg.DataSize + " bytes] " + " " + addressStr + ":" + data);
this.appendText(", ProtocolID=" + msg.ProtocolID);
this.appendText(", RxStatus=" + msg.RxStatus);
this.appendText(", TxFlags=" + msg.TxFlags);
this.appendText(", Timestamp=" + msg.Timestamp);
this.appendText(", ExtraDataIndex=" + msg.ExtraDataIndex + "\r\n");
if (msg.DataSize > 0)
{
if (prefix == "Rx" && msg.ExtraDataIndex == 0)
{
// this.parent.appendText("WARNING: ExtraDataIndex is zero, which it only shall be for pure status messages.\r\n");
}
}
}
}
}
///
/// Workaround for insufficient native thread handling in C#
///
/// Text string to append.
/// Level of message, LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR
/// 'true' if message was logged at the given log level.
private delegate bool appendTextFunc(string txt, int logLevel);
///
/// Display one raw message with header info.
///
/// Prefix to display with entry, e.g. 'Tx'
/// Message to display.
/// Address string.
/// Payload string.
private delegate void dispMsgIntFunc(string prefix, IPassThruMsg msg, string addressStr, string data);
///
/// Handle Form Closing event.
///
/// Sending object.
/// Event data.
private void LoggingForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (!this.controlledClose)
{
e.Cancel = true;
}
}
///
/// Clear the log window from old events.
///
/// Sending object.
/// Event data.
private void clearButton_Click(object sender, EventArgs e)
{
this.eventLogWindow.Text = string.Empty;
}
///
/// Clear the traffic table.
///
/// Sending object.
/// Event data.
private void clearButton2_Click(object sender, EventArgs e)
{
this.trafficDataGridView.Rows.Clear();
}
///
/// Add one row to the traffic table.
///
/// Traffic direction and layer level.
/// Address field.
/// Mode field.
/// PID field.
/// Payload length.
/// Payload data.
private delegate void addRowFunc(rowMode direction, uint address, byte mode, uint pid, uint length, string rspStr);
///
/// One item in the display queue.
///
private class DispQueueItem
{
///
/// Gets logging prefix string, e.g. "Tx".
///
public string prefix { get; private set; }
///
/// Gets message to display content of.
///
public IPassThruMsg msg { get; private set; }
///
/// Gets address string related to log event.
///
public string addressStr { get; private set; }
///
/// Gets formatted data.
///
public string data { get; private set; }
///
/// Initializes a new instance of the class.
///
/// Prefix to display with entry, e.g. 'Tx'
/// Message to display.
/// Address string.
/// Payload string.
public DispQueueItem(string prefix, IPassThruMsg msg, string addressStr, string data)
{
this.prefix = prefix;
this.msg = msg;
this.addressStr = addressStr;
this.data = data;
}
}
}
}