//----------------------------------------------------------------------- // // 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 { using System; using System.Collections.Generic; using System.ComponentModel; using System.IO.Ports; using System.Linq; using System.Management; using System.Windows.Forms; using global::DataSource; using global::DataSource.FileAccess; using global::SharedObjects; using global::SharedObjects.DataMgmt; using global::SharedObjects.Misc; using global::UserInterface.GUI.Popup; /// /// Preferences panel declaration. /// public partial class PreferencesPanel : UserControl, IPreferences { /// /// Refresh interval alternatives. /// private static readonly string[] refreshIntervalArray = { "1:1", "1:10", "1:30", "1:60", "1:300", }; /// /// Gets Refresh interval alternatives. /// public IList refreshIntervals { get { return refreshIntervalArray.ToList(); } } /// /// Gets The currently selected unit category. (0=General, 1=Metric, 2=Imperial.) /// /// Notice that "General" means that data will be presented in both metric and imperial. /// /// public int selectedUnitCategory { get; private set; } /// /// Gets default timeout value. /// public int defaultTimeout { get; private set; } /// /// Gets threshold for when a secondary Y axis shall be used in charts. /// public int secondaryYThreshold { get; private set; } /// /// Gets OBD code database URI string. /// public string obdCodeDatabase { get; private set; } /// /// Gets a value indicating whether hints shall be enabled (when false) or disabled (when true). /// public bool advancedMode { get; private set; } /// /// Gets a value indicating whether features useful for development shall be available. /// /// Notice that some of the features may be dangerous. /// /// public bool developerMode { get; private set; } /// /// Gets Time in milliseconds to pause after sending a message. /// public int sendDelay { get; private set; } /// /// Gets Number of blocks accepted from peer before sending CTS. /// public byte blockSize { get; private set; } /// /// Gets Time in milliseconds to request that peer spaces it's frames with. /// public byte frameSpacing { get; private set; } /// /// Gets Name of GPS port. /// public string gpsSerialPortName { get; private set; } /// /// Preferences file access instance. /// private PreferencesFileAccess preferencesFileAccess; /// /// Data source interface instance. /// private IDataSource iDataSource; /// /// The beginners hint engine. /// private HintEngine hintEngine; /// /// Event logging instance. /// private ILogging iLogging; /// /// Gets preferences list instance. /// public IDictionary preferences { get { return this.preferencesFileAccess.preferences; } } /// /// Initializes a new instance of the class. /// /// Current logging instance. /// Data file directory path. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Reviewed.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "CanApp.CustomToolTip", Justification = "Reviewed.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1118:ParameterMustNotSpanMultipleLines", Justification = "Reviewed.")] public PreferencesPanel(ILogging iLogging, string dataFileDir) { this.iLogging = iLogging; this.selectedUnitCategory = 0; this.advancedMode = false; this.InitializeComponent(); this.preferencesFileAccess = new PreferencesFileAccess(iLogging, DataSourceImplementation.PREFERENCES_FILE, dataFileDir); this.preferencesFileAccess.load(); this.loadPreferences(); this.initPreferredUnits(); this.gpsSerialPortName = this.getPreference(Utils.GPS_SERIAL_PORT); this.iLogging.appendText("this.gpsSerialPortName='" + this.gpsSerialPortName + "'\r\n"); string[] portNames = SerialPort.GetPortNames(); List portInfo = new List(); using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_PnPEntity WHERE ClassGuid = '{4d36e978-e325-11ce-bfc1-08002be10318}'")) { foreach (ManagementObject queryObj in searcher.Get()) { portInfo.Add(queryObj["Caption"].ToString()); } } Array.Sort(portNames, new Utils.ComPortComparer()); int ix = 1; int selIx = -1; this.gpsSerialPortNameComboBox.Items.Clear(); this.gpsSerialPortNameComboBox.Items.Add(" Disabled "); foreach (string portName in portNames) { string dispName = portName; foreach (string caption in portInfo) { if (caption.Contains("(" + portName + ")")) { dispName = caption; break; } } this.gpsSerialPortNameComboBox.Items.Add(dispName); if (this.gpsSerialPortName != null && this.gpsSerialPortName.Trim().Length > 0 && dispName.Contains("(" + this.gpsSerialPortName + ")")) { selIx = ix; } ix++; } if (selIx >= 0) { this.gpsSerialPortNameComboBox.SelectedIndex = selIx; } else { this.gpsSerialPortNameComboBox.SelectedIndex = 0; } this.toolTip1.SetToolTip(this.advancedModeCB, "Disables the application hint balloons."); this.toolTip1.SetToolTip( this.sendDelayNumericUpDown, "Time to wait after sending one message to the ECU.\r\n" + "Some ECUs can't handle full speed data traffic from the test tool."); } /// /// Register the hint engine so it can be turned on or off. /// /// Hint engine instance. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "hintEngine", Justification = "Reviewed, intentional.")] public void registerHintEngine(HintEngine hintEngine) { this.hintEngine = hintEngine; } /// /// Set the data source instance. /// /// Data source interface instance. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "iDataSource", Justification = "Reviewed, intentional.")] public void setDataSource(IDataSource iDataSource) { this.iDataSource = iDataSource; } /// /// Get integer preference value. /// /// Name of preference. /// Integer value of preference, returns 0 at failure. public int getIntPreference(string preferenceName) { string value = this.getPreference(preferenceName); int intValue = 0; try { intValue = Convert.ToInt32(value); } catch { } return intValue; } /// /// Get Preference value by name. /// /// Name of preference to get value for. /// String preference value. public string getPreference(string preferenceName) { string value = null; if (this.preferencesFileAccess.preferences.ContainsKey(preferenceName)) { XmlClass.preference pref = this.preferencesFileAccess.preferences[preferenceName]; value = pref.value; } return value; } /// /// Set Preference value. /// /// Name of preference. /// Preference value. public void setPreference(string preferenceName, string value) { XmlClass.preference pref; if (this.preferencesFileAccess.preferences.ContainsKey(preferenceName)) { pref = this.preferencesFileAccess.preferences[preferenceName]; pref.value = value; } else { pref = new XmlClass.preference(); pref.name = preferenceName; pref.value = value; this.preferencesFileAccess.preferences.Add(preferenceName, pref); } this.preferencesFileAccess.save(); } /// /// Load the preferences from file. /// private void loadPreferences() { this.selectedUnitCategory = this.getIntPreference(PreferencesFileAccess.UNITS); string site = this.getPreference(PreferencesFileAccess.OBDINFOSITE); if (site == null || site.Trim().Length == 0) { site = Utils.DEFAULT_OBD_CODE_SITE; } this.obdCodeDatabaseURI.Text = site; this.obdCodeDatabase = site; this.defaultTimeout = this.getIntPreference(PreferencesFileAccess.DEFAULT_TIMEOUT); if (this.defaultTimeout < this.timeoutUD.Minimum || this.defaultTimeout > this.timeoutUD.Maximum) { this.defaultTimeout = 1000; this.setPreference(PreferencesFileAccess.DEFAULT_TIMEOUT, this.defaultTimeout.ToString()); } if (this.defaultTimeout >= this.timeoutUD.Minimum && this.defaultTimeout <= this.timeoutUD.Maximum) { this.timeoutUD.Value = this.defaultTimeout; } this.secondaryYThreshold = this.getIntPreference(PreferencesFileAccess.SECONDARY_Y_THRESHOLD); if (this.secondaryYThreshold >= this.secondaryYThresholdUD.Minimum && this.secondaryYThreshold <= this.secondaryYThresholdUD.Maximum) { this.secondaryYThresholdUD.Value = this.secondaryYThreshold; } int flag = this.getIntPreference(PreferencesFileAccess.ADVANCED_MODE); this.advancedMode = flag != 0; this.advancedModeCB.Checked = this.advancedMode; if (this.hintEngine != null) { this.hintEngine.activate(!this.advancedMode); } this.sendDelay = this.getIntPreference(PreferencesFileAccess.SEND_DELAY); // Error means that we probably are out of range. try { this.sendDelayNumericUpDown.Value = this.sendDelay; } catch { // Default to 20ms. this.sendDelayNumericUpDown.Value = 20; } this.blockSize = (byte)this.getIntPreference(PreferencesFileAccess.BLOCK_SIZE); // Error means that we probably are out of range. try { this.blockSizeNumericUpDown.Value = this.blockSize; } catch { // Default to 3 frames. this.blockSizeNumericUpDown.Value = 3; this.blockSize = 3; } this.frameSpacing = (byte)this.getIntPreference(PreferencesFileAccess.FRAME_SPACING); // Error means that we probably are out of range. try { this.frameSpacingNumericUpDown.Value = this.frameSpacing; } catch { // Default to 20 milliseconds. this.frameSpacingNumericUpDown.Value = 20; this.frameSpacing = 20; } } /// /// Preferred units changed event. /// /// Sending object. /// Event data. private void preferredUnits_SelectedIndexChanged(object sender, EventArgs e) { ComboBox cb = (ComboBox)sender; this.selectedUnitCategory = cb.SelectedIndex; this.setPreference(PreferencesFileAccess.UNITS, this.selectedUnitCategory.ToString()); if (this.iDataSource != null) { this.iDataSource.loadPidgroups(); } } /// /// Initialize the preferred units. /// private void initPreferredUnits() { this.preferredUnits.Items.Clear(); foreach (string str in Utils.unitCategories) { this.preferredUnits.Items.Add(str); } if (this.selectedUnitCategory >= 0 && this.selectedUnitCategory < this.preferredUnits.Items.Count) { this.preferredUnits.SelectedIndex = this.selectedUnitCategory; } } /// /// Timeout value has been changed by the user. /// /// Sending object. /// Event data. private void timeoutUD_ValueChanged(object sender, EventArgs e) { this.defaultTimeout = (int)this.timeoutUD.Value; this.setPreference(PreferencesFileAccess.DEFAULT_TIMEOUT, this.defaultTimeout.ToString()); } /// /// Validate the URI. /// /// Sending object. /// Event data. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "System.Uri", Justification = "Reviewed, intentional.")] private void obdCodeDatabaseURI_Validating(object sender, CancelEventArgs e) { string txt = this.obdCodeDatabaseURI.Text; try { new Uri(txt); } catch (UriFormatException ex) { string errtxt = ex.Message + "\r\n"; errtxt += "Click OK to go back to old (default) value, Cancel to edit the current invalid value."; if (MessageBox.Show( errtxt, "Error", MessageBoxButtons.OKCancel, MessageBoxIcon.Error, MessageBoxDefaultButton.Button2) == DialogResult.Cancel) { e.Cancel = true; } else { this.obdCodeDatabaseURI.Text = this.obdCodeDatabase; } } } /// /// When URI has been validated. /// /// Sending object. /// Event data. private void obdCodeDatabaseURI_Validated(object sender, EventArgs e) { this.obdCodeDatabase = this.obdCodeDatabaseURI.Text; } /// /// Test the URI. /// /// Sending object. /// Event data. private void uriTestButton_Click(object sender, EventArgs e) { try { System.Diagnostics.Process.Start(this.obdCodeDatabase + "P0420"); } catch (Win32Exception ex) { MessageBox.Show( "'" + this.obdCodeDatabase + "'\r\n" + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } /// /// Changes of the Secondary Y axis threshold. /// /// Sending object. /// Event data. private void secondaryYThresholdUD_ValueChanged(object sender, EventArgs e) { this.secondaryYThreshold = (int)this.secondaryYThresholdUD.Value; this.setPreference(PreferencesFileAccess.SECONDARY_Y_THRESHOLD, this.secondaryYThreshold.ToString()); } /// /// Handle checkbox toggle. /// /// Sending object. /// Event data. private void advancedModeCB_CheckedChanged(object sender, EventArgs e) { this.advancedMode = this.advancedModeCB.Checked; this.setPreference(PreferencesFileAccess.ADVANCED_MODE, this.advancedMode ? "1" : "0"); if (this.hintEngine != null) { this.hintEngine.activate(!this.advancedMode); } } /// /// Handle change of GPS serial port name. /// /// Sending object. /// Event data. private void gpsSerialPortNameComboBox_SelectedIndexChanged(object sender, EventArgs e) { string str = this.gpsSerialPortNameComboBox.Text; string s2 = null; if (str.StartsWith(" ")) { s2 = string.Empty; } else { int ix = str.IndexOf("(COM"); if (ix >= 0) { s2 = str.Substring(ix + 1); int ix2 = s2.IndexOf(')'); s2 = s2.Substring(0, ix2); } else { if (str.StartsWith("COM")) { s2 = str; } } this.iLogging.appendText("Port='" + s2 + "'\r\n"); } if (s2 != null) { this.gpsSerialPortName = s2; this.setPreference(Utils.GPS_SERIAL_PORT, this.gpsSerialPortName); } } /// /// Toggle developer mode. /// /// Sending object. /// Event data. private void developerModeCB_CheckedChanged(object sender, EventArgs e) { this.developerMode = this.developerModeCB.Checked; this.setPreference(PreferencesFileAccess.DEVELOPER_MODE, this.developerMode ? "1" : "0"); } /// /// Handle change of send delay. /// /// Sending object. /// Event data. private void sendDelayNumericUpDown_ValueChanged(object sender, EventArgs e) { NumericUpDown numericUpDown = (NumericUpDown)sender; this.sendDelay = (int)numericUpDown.Value; this.setPreference(PreferencesFileAccess.SEND_DELAY, this.sendDelay.ToString()); } /// /// Handle change of block size. /// /// Sending object. /// Event data. private void blockSizeNumericUpDown_ValueChanged(object sender, EventArgs e) { NumericUpDown numericUpDown = (NumericUpDown)sender; this.blockSize = (byte)numericUpDown.Value; this.setPreference(PreferencesFileAccess.BLOCK_SIZE, this.blockSize.ToString()); } /// /// Handle change of frame spacing. /// /// Sending object. /// Event data. private void frameSpacingNumericUpDown_ValueChanged(object sender, EventArgs e) { NumericUpDown numericUpDown = (NumericUpDown)sender; this.frameSpacing = (byte)numericUpDown.Value; this.setPreference(PreferencesFileAccess.FRAME_SPACING, this.frameSpacing.ToString()); } } }