//----------------------------------------------------------------------- // // 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 Gauges { using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Globalization; using System.Windows.Forms; using global::SharedObjects.Misc; /// /// Class for editing details for a gauge. /// public partial class GaugeDetails : Form { /// /// Gets gauge item instance. /// public XmlClass.gaugeitem gaugeitem { get; private set; } /// /// Gets PID name. /// public string pidName { get; private set; } /// /// Gets PID name. /// public string digitalPidName { get; private set; } /// /// List of PID groups. /// private IList pidgrouplist; /// /// Current PID group. /// private XmlClass.pidgroup pidgroup; /// /// Current PID group. /// private XmlClass.pidgroup digitalPidgroup; /// /// Current PID. /// private XmlClass.pidgroup.pidlist pid; /// /// Current Digital PID. /// private XmlClass.pidgroup.pidlist digitalPid; /// /// Current sensor. /// private XmlClass.pidgroup.pidlist.sensordata sensor; /// /// Current sensor. /// private XmlClass.pidgroup.pidlist.sensordata digitalSensor; /// /// Number of major scale steps on gauge. /// private uint majorScaleSteps; /// /// Number of minor scale steps on gauge. /// private uint minorScaleSteps; /// /// What to divide the value with to get a reasonable presentation. /// /// E.g. engine RPM makes sense to divide with 100. /// /// private float divisor; /// /// What to divide the value with to get a reasonable presentation. /// /// E.g. engine RPM makes sense to divide with 100. /// /// private float digitalDivisor; /// /// Array of used gauges, used to avoid double-booking gauges. /// private uint[] usedItems; /// /// Fallback digital format. /// private string defaultDigitalFormat = "000.00"; /// /// Initializes a new instance of the class. /// /// PID group list. /// Gauge item. /// The number the gauge has. /// Array of used gauges to avoid assigning the same gauge twice. public GaugeDetails(IList pidgrouplist, XmlClass.gaugeitem gaugeitem, uint gaugeNumber, uint[] usedItems) { this.pidgrouplist = pidgrouplist; this.gaugeitem = gaugeitem; this.usedItems = usedItems; if (this.gaugeitem == null) { this.gaugeitem = new XmlClass.gaugeitem(); } this.InitializeComponent(); this.gaugeitem.gaugeNumber = gaugeNumber; this.gaugeNumTB.Text = gaugeNumber.ToString(); this.pidGroupsCB.Items.Clear(); this.itemCB.Items.Clear(); this.sensorCB.Items.Clear(); this.digitalPidGroupsCB.Items.Clear(); this.digitalItemCB.Items.Clear(); this.digitalSensorCB.Items.Clear(); this.pidGroupsCB.SelectedIndex = -1; this.digitalPidGroupsCB.SelectedIndex = -1; int pidGrpIx = -1; int digitalPidGrpIx = -1; int i = 0; this.digitalPidGroupsCB.Items.Add(" Disabled "); foreach (XmlClass.pidgroup pidGrp in this.pidgrouplist) { this.pidGroupsCB.Items.Add(pidGrp.name); this.digitalPidGroupsCB.Items.Add(pidGrp.name); if (this.gaugeitem != null && this.gaugeitem.pidgroupid == pidGrp.id) { pidGrpIx = i; } if (this.gaugeitem != null && this.gaugeitem.digitalPidgroupid == pidGrp.id) { digitalPidGrpIx = i + 1; } i++; } if (this.pidGroupsCB.Items.Count > 0) { if (pidGrpIx >= 0 && pidGrpIx < this.pidGroupsCB.Items.Count) { this.pidGroupsCB.SelectedIndex = pidGrpIx; } else { this.pidGroupsCB.SelectedIndex = 0; } if (digitalPidGrpIx >= 0 && digitalPidGrpIx < this.digitalPidGroupsCB.Items.Count) { this.digitalPidGroupsCB.SelectedIndex = digitalPidGrpIx; } else { this.digitalPidGroupsCB.SelectedIndex = 0; } } if (this.gaugeitem != null) { this.majorScaleSteps = this.gaugeitem.majorScaleSteps; this.minorScaleSteps = this.gaugeitem.minorScaleSteps; this.divisor = (this.gaugeitem.divisor == 0) ? 1.0F : this.gaugeitem.divisor; this.digitalDivisor = (this.gaugeitem.digitalDivisor == 0) ? 1.0F : this.gaugeitem.digitalDivisor; this.gaugeTextTB.Text = this.gaugeitem.gaugetext; this.digitalGaugeTextTB.Text = this.gaugeitem.digitalGaugetext; this.digitalFormat.Text = string.IsNullOrEmpty(this.gaugeitem.digitalFormat) ? "000.00" : this.gaugeitem.digitalFormat; } else { this.majorScaleSteps = 10; this.minorScaleSteps = 3; this.divisor = 1; this.digitalDivisor = 1; this.gaugeTextTB.Text = string.Empty; this.digitalGaugeTextTB.Text = string.Empty; this.digitalFormat.Text = "000.00"; } this.defaultDigitalFormat = this.digitalFormat.Text; this.divisorTextbox.Text = this.divisor.ToString(); this.digitalDivisorTextbox.Text = this.digitalDivisor.ToString(); this.majorScaleStepsTB.Text = this.majorScaleSteps.ToString(); this.minorScaleStepsTB.Text = this.minorScaleSteps.ToString(); } /// /// Numeric validation of input data. /// /// Sending object. /// Event data. /// Parsed numeric value. private static uint numericIntValidation(object sender, CancelEventArgs e) { TextBox tb = (TextBox)sender; uint scaleSteps = 0; try { scaleSteps = Convert.ToUInt32(tb.Text); } catch { MessageBox.Show( "Field must be numeric.", "Data Failure", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); e.Cancel = true; } return scaleSteps; } /// /// Test for float value. /// /// Sending object. /// Event data. /// Parsed float value. private static float numericFloatValidation(object sender, CancelEventArgs e) { TextBox tb = (TextBox)sender; float value = 0; try { value = Convert.ToSingle(tb.Text); } catch { MessageBox.Show( "Field must be numeric.", "Data Failure", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); e.Cancel = true; } return value; } /// /// PidGroup combobox change. /// /// Current PID group. /// Combobox that changed. /// List of valid PID groups. /// Offset value in combo box index. /// New PID group or 'null' if not applicable. private static XmlClass.pidgroup pidGroupChange( XmlClass.pidgroup pidgroup, ComboBox cb, IList pidgrouplist, int offset) { if (cb.SelectedIndex >= offset && cb.SelectedIndex < cb.Items.Count) { pidgroup = pidgrouplist[cb.SelectedIndex - offset]; } else { if (offset == 1) { pidgroup = null; } } return pidgroup; } /// /// Update Item Combo Box. /// /// Current PID group. /// Items combobox /// Gauge PID id private static void updateItemCB(XmlClass.pidgroup pidgroup, ComboBox itemCB, uint gaugePid_int) { int pidIx = -1; int i = 0; foreach (XmlClass.pidgroup.pidlist pidItem in pidgroup.pids) { itemCB.Items.Add(pidItem.name); if (gaugePid_int == pidItem.pid_int) { pidIx = i; } i++; } if (pidIx >= 0 && pidIx < itemCB.Items.Count) { itemCB.SelectedIndex = pidIx; } else { if (itemCB.Items.Count > 0) { itemCB.SelectedIndex = 0; } } } /// /// Update item combo box. /// /// Combo box. /// PID to get items for. /// Sensor combo box instance. /// Current list of PIDs /// Current gauge item. /// Name of selected PID. private static string itemCbChange( ComboBox cb, ref XmlClass.pidgroup.pidlist pid, ComboBox sensorCB, List pids, XmlClass.gaugeitem gaugeitem) { string pidName = string.Empty; sensorCB.Items.Clear(); pid = pids[cb.SelectedIndex]; pidName = pid.name; int sensorIx = -1; int i = 0; foreach (XmlClass.pidgroup.pidlist.sensordata sensorItem in pid.sensors) { string sensorInfo = string.Empty; if (sensorItem.name != null) { sensorInfo += sensorItem.name + " "; } sensorInfo += "(" + sensorItem.unit + ")"; sensorCB.Items.Add(sensorInfo); int sensorBit = -1; if (sensorItem.unit == "bit" || sensorItem.unit == "bits") { try { sensorBit = Convert.ToInt32(sensorItem.startbit); } catch { } } if (gaugeitem != null && gaugeitem.sensor_unit != null && gaugeitem.sensor_unit.Equals(sensorItem.unit) && gaugeitem.sensor_offset == sensorItem.offset && (sensorBit == -1 || gaugeitem.sensor_startbit == sensorBit)) { sensorIx = i; } i++; } if (sensorIx >= 0 && sensorIx < sensorCB.Items.Count) { sensorCB.SelectedIndex = sensorIx; } else { if (sensorCB.Items.Count > 0) { sensorCB.SelectedIndex = 0; } } return pidName; } /// /// OK button clicked. /// /// Sending object. /// Event data. private void okButton_Click(object sender, EventArgs e) { this.gaugeitem.pidgroupid = this.pidgroup.id; this.gaugeitem.pid_int = this.pid.pid_int; this.gaugeitem.sensor_unit = this.sensor.unit; this.gaugeitem.sensor_offset = this.sensor.offset; this.gaugeitem.majorScaleSteps = this.majorScaleSteps; this.gaugeitem.minorScaleSteps = this.minorScaleSteps; this.gaugeitem.gaugetext = this.gaugeTextTB.Text; this.gaugeitem.divisor = this.divisor; int sensorBit = -1; try { sensorBit = Convert.ToInt32(this.sensor.startbit); } catch { } this.gaugeitem.sensor_startbit = sensorBit; if (this.digitalPidgroup != null) { this.gaugeitem.digitalPidgroupid = this.digitalPidgroup.id; this.gaugeitem.digitalPid_int = this.digitalPid.pid_int; this.gaugeitem.digitalSensor_unit = this.digitalSensor.unit; this.gaugeitem.digitalSensor_offset = this.digitalSensor.offset; this.gaugeitem.digitalGaugetext = this.digitalGaugeTextTB.Text; this.gaugeitem.digitalDivisor = this.digitalDivisor; this.gaugeitem.digitalFormat = this.digitalFormat.Text; int digitalSensorBit = -1; try { digitalSensorBit = Convert.ToInt32(this.digitalSensor.startbit); } catch { } this.gaugeitem.digitalSensor_startbit = digitalSensorBit; } else { this.gaugeitem.digitalPidgroupid = 0; this.gaugeitem.digitalPid_int = 0; this.gaugeitem.digitalSensor_unit = string.Empty; this.gaugeitem.digitalSensor_offset = 0; this.gaugeitem.digitalGaugetext = string.Empty; this.gaugeitem.digitalDivisor = 1; this.gaugeitem.digitalFormat = "000.0"; this.gaugeitem.digitalSensor_startbit = -1; } this.DialogResult = DialogResult.OK; this.Close(); } /// /// Cancel button clicked. /// /// Sending object. /// Event data. private void cancelButton_Click(object sender, EventArgs e) { this.DialogResult = DialogResult.Cancel; this.Close(); } /// /// Scale text box changed. /// /// Sending object. /// Event data. private void scaleTextbox_Validating(object sender, CancelEventArgs e) { this.majorScaleSteps = numericIntValidation(sender, e); } /// /// PID group changed. /// /// Sending object. /// Event data. private void pidGroupsCB_SelectedIndexChanged(object sender, EventArgs e) { this.pidgroup = pidGroupChange( this.pidgroup, (ComboBox)sender, this.pidgrouplist, 0); this.itemCB.Items.Clear(); this.sensorCB.Items.Clear(); this.pidName = string.Empty; if (this.pidgroup != null) { updateItemCB(this.pidgroup, this.itemCB, this.gaugeitem.pid_int); } } /// /// PID group changed. /// /// Sending object. /// Event data. private void digitalPidGroupsCB_SelectedIndexChanged(object sender, EventArgs e) { this.digitalPidgroup = pidGroupChange( this.digitalPidgroup, (ComboBox)sender, this.pidgrouplist, 1); this.digitalItemCB.Items.Clear(); this.digitalSensorCB.Items.Clear(); this.digitalPidName = string.Empty; if (this.digitalPidgroup != null) { updateItemCB(this.digitalPidgroup, this.digitalItemCB, this.gaugeitem.digitalPid_int); } } /// /// Item combo box changed. /// /// Sending object. /// Event data. private void itemCB_SelectedIndexChanged(object sender, EventArgs e) { ComboBox cb = (ComboBox)sender; if (this.pidgroup != null && cb.SelectedIndex >= 0 && cb.SelectedIndex < cb.Items.Count) { this.pidName = itemCbChange(cb, ref this.pid, this.sensorCB, this.pidgroup.pids, this.gaugeitem); } } /// /// Selection of item for digital gauge changed. /// /// Sending object. /// Event data. private void digitalItemCB_SelectedIndexChanged(object sender, EventArgs e) { ComboBox cb = (ComboBox)sender; if (this.digitalPidgroup != null && cb.SelectedIndex >= 0 && cb.SelectedIndex < cb.Items.Count) { this.digitalPidName = itemCbChange(cb, ref this.digitalPid, this.digitalSensorCB, this.digitalPidgroup.pids, this.gaugeitem); } } /// /// Sensor combo box changed. /// /// Sending object. /// Event data. private void sensorCB_SelectedIndexChanged(object sender, EventArgs e) { ComboBox cb = (ComboBox)sender; if (cb.SelectedIndex >= 0 && cb.SelectedIndex < this.itemCB.Items.Count) { this.sensor = this.pid.sensors[cb.SelectedIndex]; } } /// /// Sensor combo box changed. /// /// Sending object. /// Event data. private void digitalSensorCB_SelectedIndexChanged(object sender, EventArgs e) { ComboBox cb = (ComboBox)sender; if (cb.SelectedIndex >= 0 && cb.SelectedIndex < this.itemCB.Items.Count) { this.digitalSensor = this.digitalPid.sensors[cb.SelectedIndex]; } } /// /// Divisor text box changed. /// /// Sending object. /// Event data. private void divisorTextbox_Validating(object sender, CancelEventArgs e) { this.divisor = numericFloatValidation(sender, e); } /// /// Divisor text box changed. /// /// Sending object. /// Event data. private void digitalDivisorTextbox_Validating(object sender, CancelEventArgs e) { this.digitalDivisor = numericFloatValidation(sender, e); } /// /// Minor scale steps changed. /// /// Sending object. /// Event data. private void minorScaleStepsTB_Validating(object sender, CancelEventArgs e) { this.minorScaleSteps = numericIntValidation(sender, e); } /// /// Select gauge. /// /// Sending object. /// Event data. private void button1_Click(object sender, EventArgs e) { GaugeSelector gs = new GaugeSelector(this.gaugeitem.gaugeNumber, this.usedItems); try { if (gs.ShowDialog() == System.Windows.Forms.DialogResult.OK && gs.selectedItem != null) { this.gaugeitem.gaugeNumber = (uint)gs.selectedItem; this.gaugeNumTB.Text = this.gaugeitem.gaugeNumber.ToString(); } } finally { gs.Dispose(); } } /// /// Validate format mask. /// /// Sending object. /// Event data. private void digitalFormat_Validating(object sender, CancelEventArgs e) { TextBox tb = (TextBox)sender; string mask = tb.Text; tb.BackColor = Color.White; try { string mask1 = mask.Replace("0", string.Empty); mask1 = mask1.Replace(".", string.Empty); if (mask1.Length > 0) { throw new Exception("Only '0' (zero) and '.' are permitted."); } string[] sa = mask.Split(new char[] { '.' }); if (sa.Length > 2) { throw new Exception("At most one '.' is permitted."); } // Finish with an attempt to apply the format mask to a value too. float number = (float)Math.PI; string num = number.ToString(mask, CultureInfo.InvariantCulture); if (num.Length == 0) { throw new Exception("Formatting with mask '" + mask + "' produced an empty string."); } } catch (Exception ex) { tb.BackColor = Color.LightPink; if (MessageBox.Show("Invalid format mask '" + mask + "'\r\n" + ex.Message + "\r\n", "Error", MessageBoxButtons.OKCancel, MessageBoxIcon.Hand) == System.Windows.Forms.DialogResult.OK) { e.Cancel = true; } else { tb.BackColor = Color.White; tb.Text = this.defaultDigitalFormat; } } } } }