//-----------------------------------------------------------------------
//
// 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 CanApp.CAN
{
using System;
using System.Collections;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using global::DeviceApi;
using global::SharedObjects;
using global::SharedObjects.Api;
using global::SharedObjects.Misc;
using global::SharedObjects.Protocol;
///
/// Form for presentation of raw CAN data frames and logging of them to file.
///
public partial class DataListForm : Form, IRawReceiver
{
///
/// Logger instance.
///
private ILogging iLogging;
///
/// Functions instance.
///
private IMiscFunc iMiscFunc;
///
/// Table with known CAN addresses.
///
private Hashtable addressTable = new Hashtable();
///
/// Strings for seconds.
///
private string secondsStr = string.Empty;
///
/// Lock object to manage concurrent writes to CAN log file.
///
private object lockObject = new object();
///
/// Message counter.
///
private long messageCounter = 0;
///
/// Message handler instance.
///
private IMessageHandler messageHandler;
///
/// Filter Handler instance.
///
private IFilterHandler filterHandler;
///
/// Thread injecting test messages, only for application testing.
///
private Thread testMsgThread = null;
///
/// Flag indicating that the test thread shall run.
///
private bool doRun;
///
/// If the data in the log window shall be appended instead of refreshed for each address encountered.
///
private bool appendLogging = false;
///
/// Initializes a new instance of the class.
///
/// Logger instance
/// Functions instance.
/// Current filter handler instance.
/// Message handler instance.
public DataListForm(
ILogging iLogging,
IMiscFunc iMiscFunc,
IFilterHandler filterHandler,
IMessageHandler messageHandler)
{
this.iLogging = iLogging;
this.iMiscFunc = iMiscFunc;
this.filterHandler = filterHandler;
this.messageHandler = messageHandler;
this.InitializeComponent();
this.appendLoggingCB.Checked = this.appendLogging;
this.messageHandler.setRawReceiver(this);
}
///
/// Receive one message from the message handler.
///
/// Received message.
/// Return false to inhibit further processing.
public bool rawHandler(IPassThruMsg rxmsg)
{
this.dispMsg(this.messageCounter++, rxmsg);
// We are the only one receiving these messages.
return false;
}
///
/// Add data to the trace data data grid view table.
///
/// Counter value.
/// Timestamp value.
/// Message ID (Address)
/// Data String.
/// Data size value.
/// Index of extra data.
/// 'true' if successful.
private bool setData(long counter, string timestamp, string msgIdStr, string valueData, int dataSize1, int extraDataIndex1)
{
bool success = false;
if (this.InvokeRequired)
{
try
{
success = (bool)Invoke(new setDataFunc(this.setData), new object[] { counter, timestamp, msgIdStr, valueData, dataSize1, extraDataIndex1 });
}
catch (System.Reflection.TargetParameterCountException ex)
{
MessageBox.Show(
"Exception: " + ex.Message + "\r\nmsgIdStr='" + msgIdStr + "'",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
}
catch (System.ObjectDisposedException)
{
// Ignore.
}
}
else
{
try
{
DataGridViewRowCollection dgvrc = this.tracedata.Rows;
if (dgvrc.Count > 1000)
{
dgvrc.RemoveAt(0);
}
int n = 0;
if (!this.appendLogging && this.addressTable.ContainsKey(msgIdStr))
{
n = (int)this.addressTable[msgIdStr];
}
else
{
n = dgvrc.Add(1);
if (!this.appendLogging)
{
this.addressTable.Add(msgIdStr, n);
}
}
DataGridViewRow dgvr = dgvrc[n];
DataGridViewCellCollection dgvcc = dgvr.Cells;
dgvcc[0].Value = counter;
dgvcc[1].Value = timestamp;
dgvcc[2].Value = msgIdStr;
dgvcc[3].Value = dataSize1.ToString("x2");
dgvcc[4].Value = extraDataIndex1.ToString("x2");
dgvcc[5].Value = valueData;
success = true;
}
catch
{
}
}
return success;
}
///
/// Handle closing of form.
///
/// Trig object.
/// Event data.
private void DataListForm_FormClosing(object sender, FormClosingEventArgs e)
{
this.stopHandler();
}
///
/// Add data to the trace data data grid view table.
///
/// Counter value.
/// Timestamp value.
/// Message ID (Address)
/// Data String.
/// Data size value.
/// Index of extra data.
/// 'true' if successful.
private delegate bool setDataFunc(long counter, string timestamp, string msgIdStr, string valueData, int dataSize, int extraDataIndex);
///
/// Display one message.
///
/// Message counter.
/// Message to display.
/// Indicates if the action was a success.
private bool dispMsg(long msgCount, IPassThruMsg rxmsg)
{
bool success = false;
if ((rxmsg.RxStatus & PassThruConstants.rxflags.START_OF_MESSAGE) != 0)
{
return true;
}
if (rxmsg.DataSize >= 4)
{
int msgId = 0;
string timestamp = rxmsg.Timestamp.ToString();
string msgIdStr = string.Empty;
for (int i = 0; i < 4; i++)
{
msgId = msgId << 8 | (int)(rxmsg.Data[i] & 0xff);
msgIdStr += string.Format("{0:x2} ", (int)(rxmsg.Data[i] & 0xff));
}
string rxData = string.Empty;
for (int i = 4; i < rxmsg.DataSize; i++)
{
if (i > 4)
{
rxData += " ";
}
rxData += string.Format("{0:x2}", (int)(rxmsg.Data[i] & 0xff));
}
success = this.setData(msgCount, timestamp, msgIdStr, rxData, rxmsg.DataSize, rxmsg.ExtraDataIndex);
if (success)
{
TextWriter out1 = this.iLogging.getCanDataLogWriter();
if (out1 != null)
{
long t1 = DateTime.Now.Ticks;
this.secondsStr = this.iMiscFunc.getSecondsStr(t1);
// Make sure that we don't get mixed data.
lock (this.lockObject)
{
out1.Write(this.secondsStr);
out1.Write(" 1 "); // CAN Channel number, always 1.
out1.Write(string.Format("{0:x}", msgId).PadRight(16, ' '));
out1.Write("Tx "); // Always 'Tx' for now.
out1.Write("d "); // 'd' for Data.
out1.Write(string.Format("{0:x}", rxmsg.DataSize - 4) + " "); // Number of data bytes.
out1.WriteLine(rxData); // Data
out1.Flush(); // If we crash.
}
}
}
}
return success;
}
///
/// Handle a button click.
///
/// Sending button.
/// Event data.
private void addTxtBT_Click(object sender, EventArgs e)
{
TextWriter out1 = this.iLogging.getCanDataLogWriter();
if (out1 != null)
{
// Make sure that we don't get mixed data.
lock (this.lockObject)
{
out1.WriteLine(this.secondsStr + " Comment: 0 " + this.textBox1.Text); // Should work in ASC file, needs to be tested.
}
}
}
///
/// Handle a button click.
///
/// Sending button.
/// Event data.
private void startBT_Click(object sender, EventArgs e)
{
this.filterHandler.setRawLoggerFilter();
this.messageHandler.startThread();
this.iLogging.appendText("Thread started.\r\n");
this.startBT.Enabled = false;
this.stopBT.Enabled = true;
}
///
/// Handle a button click.
///
/// Sending button.
/// Event data.
private void stopBT_Click(object sender, EventArgs e)
{
this.stopHandler();
this.iLogging.appendText("Thread stopped.\r\n");
this.startBT.Enabled = true;
this.stopBT.Enabled = false;
}
///
/// Handle shut down of module.
///
private void stopHandler()
{
this.messageHandler.stopThread();
}
///
/// Insert a test message.
///
/// In reality only useful for development of the application.
///
///
/// Counter value.
/// Timeout value.
private void testMessage(long e1, int timeout)
{
IPassThruMsg rxmsg1 = PassThruMsg.getMaskedMsg(Protocols.CAN);
rxmsg1.Timestamp = 1663505644;
rxmsg1.DataSize = 7;
Random random = new Random();
int randomNumber = random.Next(0, 6);
rxmsg1.Data[3] = (byte)(randomNumber);
rxmsg1.Data[4] = (byte)1;
rxmsg1.Data[5] = (byte)2;
rxmsg1.Data[6] = (byte)3;
this.dispMsg(e1, rxmsg1);
Thread.Sleep(timeout);
return;
}
///
/// Thread that generates test messages.
///
private void testThread()
{
this.doRun = true;
long e1 = 0;
while (this.doRun)
{
int timeout = (int)this.timeoutUD.Value;
this.testMessage(e1++, timeout);
}
}
///
/// Handle checkbox change.
///
/// Sender object.
/// Event data.
private void testCB_CheckedChanged(object sender, EventArgs e)
{
CheckBox cb = (CheckBox)sender;
if (cb.Checked)
{
this.testMsgThread = new Thread(new ThreadStart(this.testThread));
this.testMsgThread.Name = "Test Msg";
this.testMsgThread.Start();
}
else
{
this.doRun = false;
this.testMsgThread.Abort();
this.testMsgThread.Interrupt();
this.testMsgThread.Join(4000);
this.testMsgThread = null;
}
}
///
/// Handle changes of the checkbox state.
///
/// Sender object.
/// Event data.
private void appendLoggingCB_CheckedChanged(object sender, EventArgs e)
{
this.appendLogging = this.appendLoggingCB.Checked;
}
}
}