//-----------------------------------------------------------------------
//
// 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 UserInterface.GUI.OBD
{
using System;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using global::Protocol.OBD;
using global::SharedObjects;
using global::SharedObjects.DataMgmt;
using global::SharedObjects.GUI;
using global::SharedObjects.Misc;
using global::SharedObjects.Misc.Objects;
using global::SharedObjects.Protocol;
using global::SharedObjects.Protocol.OBD;
///
/// Panel for probing of possible PID values in a Mode.
///
public partial class ProbePidsPanel : AbstractUserControl, IDataPanel
{
///
/// Color for items that was detected with a decent response.
///
private static readonly Color OK_COLOR = Color.LightGreen;
///
/// Color for items that did an error response.
///
private static readonly Color ERR_COLOR = Color.LightPink;
///
/// Color for items that timed out.
///
private static readonly Color TIMEOUT_COLOR = Color.LightYellow;
///
/// Thread for probing.
///
private Thread probeThread = null;
///
/// Flag to indicate if thread shall be executing.
///
private bool doRun = true;
///
/// Lock object for synchronizing.
///
private object lockObject = new object();
///
/// Timeout flag.
///
private bool timeout = true;
///
/// Error flag.
///
private bool error = false;
///
/// Messaging interface instance.
///
private IMessaging iMessaging;
///
/// Data source instance.
///
private IDataSource iDataSource;
///
/// Mode parser instance.
///
private ModeParser modeParser;
///
/// Current mode.
///
private byte mode = 0x00;
///
/// First PID to scan.
///
private uint startPid = 0x00;
///
/// Last PID to scan.
///
private uint endPid = 0x00;
///
/// Mode value valid.
///
private bool modeValid = false;
///
/// Start PID valid.
///
private bool startValid = false;
///
/// End PID valid.
///
private bool endValid = false;
///
/// Flag indicating that all items shall be displayed regardless of they are found or not.
///
private bool showAll = false;
///
/// Initializes a new instance of the class.
///
/// Event logging instance.
/// Current Vehicle.
/// Current Application Tree instance.
/// Messaging interface instance.
/// Data source instance.
/// Mode parser instance.
public ProbePidsPanel(
ILogging iLogging,
XmlClass.vehicle vehicle,
IApplicationTree applicationTree,
IMessaging iMessaging,
IDataSource iDataSource,
ModeParser modeParser)
: base(iLogging, vehicle, applicationTree)
{
this.iMessaging = iMessaging;
this.iDataSource = iDataSource;
this.modeParser = modeParser;
this.InitializeComponent();
this.showAll = this.showCheckbox.Checked;
foreach (XmlClass.modeitem modeitem in this.iDataSource.modes)
{
this.modeComboBox.Items.Add("0x" + modeitem.mode_int.ToString("x2") + " " + modeitem.name);
}
}
///
/// Perform parsing of data.
///
/// Message mode.
/// Actual data payload.
/// Related transmitted message to help with parsing.
/// Pop next request and send.
/// Source address of received data.
/// Detected protocol by ELM/AGV adapter.
/// 'true' if successfully parsed by panel.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "payload", Justification = "Reviewed.")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "mode", Justification = "Reviewed.")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "error", Justification = "Reviewed.")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "3#", Justification = "Reviewed.")]
public bool parse(uint mode, byte[] payload, IRequestData requestData, ref bool popNext, uint srcAddress, uint detectedProtocol)
{
#if TRACE
this.iLogging.appendText("mode=0x" + mode.ToString("x2") + "\r\n");
#endif
bool error = false;
if (mode == 0x7f)
{
error = true;
}
else
{
}
lock (this.lockObject)
{
this.error = error;
this.timeout = false;
Monitor.PulseAll(this.lockObject);
}
return true;
}
///
/// Start button clicked.
///
/// Sending object.
/// Event data.
private void startButton_Click(object sender, EventArgs e)
{
this.startButtonEnabled(false);
this.stopButtonEnabled(true);
this.modeParser.probePids = true;
this.listView1.Items.Clear();
this.probeThread = new Thread(this.probeThreadImpl);
this.doRun = true;
this.probeThread.Start();
}
///
/// Stop button clicked.
///
/// Sending object.
/// Event data.
private void stopButton_Click(object sender, EventArgs e)
{
this.stopButtonEnabled(false);
this.doRun = false;
this.probeThread.Abort();
this.probeThread.Interrupt();
this.probeThread.Join(4000);
this.startButtonEnabled(true);
this.modeParser.probePids = false;
}
///
/// Implementation of Thread for probing.
///
private void probeThreadImpl()
{
foreach (XmlClass.modeitem modeitem in this.iDataSource.modes)
{
if (modeitem.mode_int == this.mode)
{
for (uint i = this.startPid; i <= this.endPid && this.doRun; i++)
{
XmlClass.pidgroup.pidlist pid = new XmlClass.pidgroup.pidlist();
pid.name = "PID 0x" + i.ToString("x2");
pid.pidIsMap = false;
pid.plotHeight = 150;
pid.refresh = "1:1";
pid.pid_int = i;
RequestData msg;
switch (this.mode)
{
case 0x02:
msg = new RequestData(null, (byte)this.mode, pid, new TxMsg(new byte[] { (byte)this.mode, (byte)(pid.pid_int & 0xff), 0x00 }));
break;
case 0x22:
msg = new RequestData(null, (byte)this.mode, pid, new TxMsg(new byte[] { (byte)this.mode, (byte)((pid.pid_int >> 8) & 0xff), (byte)(pid.pid_int & 0xff) }));
break;
case 0xa0:
msg = new RequestData(null, (byte)this.mode, pid, new TxMsg(new byte[] { (byte)this.mode, 0x00, (byte)((pid.pid_int >> 16) & 0xff), (byte)((pid.pid_int >> 8) & 0xff), (byte)(pid.pid_int & 0xff), (byte)(pid.pid_bytes) }));
break;
case 0xa8:
msg = new RequestData(null, (byte)this.mode, pid, new TxMsg(new byte[] { (byte)this.mode, 0x00, (byte)((pid.pid_int >> 16) & 0xff), (byte)((pid.pid_int >> 8) & 0xff), (byte)(pid.pid_int & 0xff) }));
break;
default:
msg = new RequestData(null, (byte)this.mode, pid, new TxMsg(new byte[] { (byte)this.mode, (byte)(pid.pid_int & 0xff) }));
break;
}
this.iMessaging.queueMsg(msg);
this.iMessaging.sendMsg();
lock (this.lockObject)
{
this.timeout = true;
Monitor.Wait(this.lockObject, 200);
ListViewItem listViewItem = new ListViewItem();
if (!this.timeout)
{
if (!this.error)
{
listViewItem.BackColor = OK_COLOR;
listViewItem.Text = "A 0x" + i.ToString("x2");
}
else
{
listViewItem.BackColor = ERR_COLOR;
listViewItem.Text = "E 0x" + i.ToString("x2");
}
}
else
{
listViewItem.BackColor = TIMEOUT_COLOR;
listViewItem.Text = "N 0x" + i.ToString("x2");
}
if (this.showAll || (!this.error && !this.timeout))
{
this.addItem(listViewItem);
}
}
}
}
if (!this.doRun)
{
break;
}
}
Thread.Sleep(1000);
this.stopButtonEnabled(false);
this.startButtonEnabled(true);
this.modeParser.probePids = false;
}
///
/// Set Stop Button enable state.
///
/// 'true' if set to enabled.
private delegate void stopButtonEnabledFunc(bool enabled);
///
/// Set Stop Button enable state.
///
/// 'true' if set to enabled.
private void stopButtonEnabled(bool enabled)
{
if (this.InvokeRequired)
{
try
{
this.Invoke(new stopButtonEnabledFunc(this.stopButtonEnabled), new object[] { enabled });
}
catch (System.Reflection.TargetParameterCountException ex)
{
MessageBox.Show(
"Exception: " + ex.Message + "\r\n",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
}
catch (System.ObjectDisposedException)
{
// Ignore.
}
}
else
{
this.stopButton.Enabled = enabled;
}
}
///
/// Add one item to the List View.
///
/// Item to add.
private delegate void addItemFunc(ListViewItem listViewItem);
///
/// Add one item to the List View.
///
/// Item to add.
private void addItem(ListViewItem listViewItem)
{
if (this.InvokeRequired)
{
try
{
this.Invoke(new addItemFunc(this.addItem), new object[] { listViewItem });
}
catch (System.Reflection.TargetParameterCountException ex)
{
MessageBox.Show(
"Exception: " + ex.Message + "\r\n",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
}
catch (System.ObjectDisposedException)
{
// Ignore.
}
}
else
{
this.listView1.Items.Add(listViewItem);
}
}
///
/// Selected index in Mode combo box updated.
///
/// Sending object.
/// Event data.
private void modeComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
ComboBox tb = (ComboBox)sender;
if (tb.Text.Trim().Length > 0)
{
try
{
this.mode = (byte)Utils.hexParse(tb.Text);
this.modeValid = true;
this.startButtonEnabled(true);
}
catch
{
}
}
}
///
/// Validate range start value.
///
/// Sending object.
/// Event data.
private void rangeStartTB_Validating(object sender, CancelEventArgs e)
{
TextBox tb = (TextBox)sender;
if (tb.Text.Trim().Length > 0)
{
try
{
this.startPid = Utils.hexParse(tb.Text);
this.startValid = true;
this.startButtonEnabled(true);
}
catch (Exception ex)
{
if (MessageBox.Show(
"Invalid data '" + tb.Text + "'\r\n" + ex.Message,
"Error",
MessageBoxButtons.OKCancel,
MessageBoxIcon.Hand) == DialogResult.OK)
{
e.Cancel = true;
tb.BackColor = Color.LightPink;
}
else
{
tb.Text = string.Empty;
}
}
}
}
///
/// Validate range end value.
///
/// Sending object.
/// Event data.
private void rangeEndTB_Validating(object sender, CancelEventArgs e)
{
TextBox tb = (TextBox)sender;
if (tb.Text.Trim().Length > 0)
{
try
{
this.endPid = Utils.hexParse(tb.Text);
this.endValid = true;
this.startButtonEnabled(true);
}
catch (Exception ex)
{
if (MessageBox.Show(
"Invalid data '" + tb.Text + "'\r\n" + ex.Message,
"Error",
MessageBoxButtons.OKCancel,
MessageBoxIcon.Hand) == DialogResult.OK)
{
e.Cancel = true;
tb.BackColor = Color.LightPink;
}
else
{
tb.Text = string.Empty;
}
}
}
}
///
/// Set Start button enabled.
///
/// 'true' if enabled.
private delegate void startButtonEnabledFunc(bool enabled);
///
/// Set Start button enabled.
///
/// 'true' if enabled.
private void startButtonEnabled(bool enabled)
{
if (this.InvokeRequired)
{
try
{
this.Invoke(new startButtonEnabledFunc(this.startButtonEnabled), new object[] { enabled });
}
catch (System.Reflection.TargetParameterCountException ex)
{
MessageBox.Show(
"Exception: " + ex.Message + "\r\n",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
}
catch (System.ObjectDisposedException)
{
// Ignore.
}
}
else
{
this.startButton.Enabled = enabled && this.endValid && this.startValid && this.modeValid;
this.saveButton.Enabled = this.startButton.Enabled && this.listView1.Items.Count > 0;
}
}
///
/// Save button clicked.
///
/// Sending object.
/// Event data.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "Reviewed.")]
private void saveButton_Click(object sender, EventArgs e)
{
this.saveFileDialog1.FileName = string.Empty;
this.saveFileDialog1.DefaultExt = ".txt";
this.saveFileDialog1.AddExtension = true;
this.saveFileDialog1.ValidateNames = true;
this.saveFileDialog1.Filter = "Text File|*.txt";
if (this.saveFileDialog1.ShowDialog() == DialogResult.OK)
{
if (!string.IsNullOrEmpty(this.saveFileDialog1.FileName))
{
System.IO.FileStream fileStream = null;
StreamWriter streamWriter = null;
try
{
fileStream = (System.IO.FileStream)this.saveFileDialog1.OpenFile();
streamWriter = new StreamWriter(fileStream, Utils.iso_8859_1);
foreach (ListViewItem listViewItem in this.listView1.Items)
{
streamWriter.WriteLine(listViewItem.Text);
}
}
catch (Exception ex)
{
MessageBox.Show(
ex.Message,
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
}
finally
{
if (streamWriter != null)
{
try
{
streamWriter.Close();
}
catch
{
}
}
if (fileStream != null)
{
try
{
fileStream.Close();
}
catch
{
}
}
}
}
}
}
///
/// Checkbox for "Show All" changed state.
///
/// Sending object.
/// Event data.
private void showCheckbox_CheckedChanged(object sender, EventArgs e)
{
CheckBox cb = (CheckBox)sender;
this.showAll = cb.Checked;
}
}
}