//----------------------------------------------------------------------- // // 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; } } }