//#define DoGetAvailableMode1PIDs
#define Debugging_Enabled


using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Diagnostics;
using System.IO;
using System.Globalization;
using Device = Microsoft.DirectX.DirectSound.Device;
using Buffer = Microsoft.DirectX.DirectSound.Buffer;
using BufferDescription = Microsoft.DirectX.DirectSound.BufferDescription;


// ensure you have read the Readme and license.txt file!

/* License:
I am releasing this app with source code under the GNU GPL (GNU General Public License) with the following terms:
- It is NOT to be used in any form for commercial purposes!
- Code may not be used in other apps for any purpose.
- Changes may be made to the code provided ALL changes are clearly marked with #region tags and \\\ documentation tags. The documentation tags
  must include:
  - the author of the change(s)
  - why the change(s) was/were made
  - the complete source code is emailed to TheDarkWraith@hotmail.com
- Additions may be added to the code provided that ALL additions are clearly marked with #region tags and \\\ documentation tags. The documentation tags
  must include:
  - the author of the addition(s)
  - why the addition(s) was/were made
  - the complete source code is emailed to TheDarkWraith@hotmail.com
- The assembly name will remain as Weber-Marelli 1_6M_ECU_Tools
- The default namespace will remain as TheDarkWraith.WM16MEV
- The icon for the main form (WM16MEV.cs) will remain as MrFX_Racerboy.ico (the current icon)
- Any new forms added will use the same icon (MrFX_Racerboy.ico)
- The target framework will remain .NET framework 2.0
- The title of the main form (WM16MEV.cs) will remain as Weber-Marelli 1.6M ECU Tools by TheDarkWraith vy where y is the current exe version as built
- Individual code files (i.e. SerialComms.cs for one) will not be used outside of the app and may NOT be used in any other app
- The code will remain C# code and WILL NOT be translated to any other computer language
- All new code files and forms will contain a copy of this license in complete in comment tags right after any using statements at start of file
- The status strip (statusStrip1) of the main form (WM16MEV.cs) WILL NOT BE DELETED OR HIDDEN and the right hand side of this status strip WILL contain the tool strip status label
  called tsslLicense WITH the following text: MAY NOT BE USED FOR COMMERCIAL PURPOSES
*/

/*
 * the diagnostics is coded and working. The only part that isn't working correctly yet is the automatic termination. When the diagnostic test finishes normally
 * the ECU sends a $EE byte out. For some reason the app is not catching this $EE byte. Thus in order to terminate the diagnostic test you'll have to click the
 * diagnostic test button you pressed to start the test.
*/

namespace TheDarkWraith.WM16MEV
{
    /// <summary>
    /// This app will allow editing/viewing the Weber-Marelli 1.6M ECU used in cars and motorcycles
    /// </summary>
    public partial class WM16MEV : Form
    {
        #region Delegates
        private delegate void RefreshThis();
        #endregion

        #region Events
        #endregion

        #region Constants
        internal const float cfueltableconversion = 0.06452f;
        internal const float csparktableconversion = 0.5f;

        private const int cEEPROMsize = 512;
        private const byte ctdwdisableall = 0xAB;
        private const byte cDumpEEPROM = 0xAC;
        private const byte cDumpByte = 0xAD;

        internal const byte cGlobalFuelTrimEnable = 0xAE;
        internal const byte cGlobalFuelTrimDisable = 0xAF;
        internal const byte cGlobalFuelTrimIncrement = 0xB0;
        internal const byte cGlobalFuelTrimDecrement = 0xB1;
        internal const byte cGlobalFuelTrimClear = 0xB2;
        internal const byte cGlobalFuelTrimGet = 0xC3;

        internal const byte cGlobalSparkTrimEnable = 0xB3;
        internal const byte cGlobalSparkTrimDisable = 0xB4;
        internal const byte cGlobalSparkTrimIncrement = 0xB5;
        internal const byte cGlobalSparkTrimDecrement = 0xB6;
        internal const byte cGlobalSparkTrimClear = 0xB7;
        internal const byte cGlobalSparkTrimGet = 0xC4;
        
        internal const byte cGlobalVertCylTrimEnable = 0xB8;
        internal const byte cGlobalVertCylTrimDisable = 0xB9;
        internal const byte cGlobalVertCylTrimIncrement = 0xBA;
        internal const byte cGlobalVertCylTrimDecrement = 0xBB;
        internal const byte cGlobalVertCylTrimClear = 0xBC;
        internal const byte cGlobalVertCylTrimGet = 0xC5;

        internal const byte cDisableTrimsEnableFuelAirTempTrim = 0xBD;
        internal const byte cDisableTrimsDisableFuelAirTempTrim = 0xBE;
        internal const byte cDisableTrimsEnableFuelWaterTempTrim = 0xBF;
        internal const byte cDisableTrimsDisableFuelWaterTempTrim = 0xC0;
        internal const byte cDisableTrimsEnableFuelAltitudeTrim = 0xC1;
        internal const byte cDisableTrimsDisableFuelAltitudeTrim = 0xC2;

        // TDW disable trims
        private const byte ctdwdisabletrims = 0x27;
        internal const int ctdwdisabletrims_enabled = 0;
        internal const int ctdwdisabletrims_fuel_airtemp_enabled = 1;
        internal const int ctdwdisabletrims_fuel_watertemp_enabled = 2;
        internal const int ctdwdisabletrims_fuel_altitude_enabled = 3;

        // TDW global trims
        private const byte ctdwglobaltrims = 0x28;
        private const int ctdwglobaltrims_initialized = 0;
        internal const int ctdwglobaltrims_fuel_enabled = 1;
        internal const int ctdwglobaltrims_spark_enabled = 2;
        internal const int ctdwglobaltrims_vertcyl_enabled = 3;

        // ECU flags
        private const byte cecuflags = 0x29;
        private const int cecuflags_supports_tdwflags = 0;
        private const int cecuflags_supports_memory_dumping = 1;
        private const int cecuflags_supports_global_trims = 2;
        private const int cecuflags_supports_disable_trims = 3;
        private const int cecuflags_supports_byte_dump = 4;
        private const int cecuflags_supports_high_speed_comms = 5;

        // TDW flags
        private const byte ctdwflags = 0x2A;
        private const int ctdwflags_powerup_tach_performed = 0;
        private const int ctdwflags_can_dump_memory = 1;
        private const int ctdwflags_dumping_memory = 2;
        private const int ctdwflags_dumping_byte = 3;

        // FGSTAT
        private const byte cfgstat = 0x13;

        // comms
        private const byte ccommslowspeed = 0xC6;           // 7812.5
        private const byte ccommshighspeed = 0xC7;          // 62500

        private const String csoundsfoldername = "Sounds";
        private const String cpicturesfoldername = "Pictures";
        private const string ciso_code = "ISO: ";
        private const string cecu_initialized = "ECU initialized: ";
        private const string crepair_code = "Part #: "; 
        private const string capp = "Weber-Marelli 1.6M ECU Tools by TheDarkWraith";
        private const int cnext_PID_sleep = 5;
        private const int cPID_receive_timeout = 300;
        private const int cstarting_poll_speed = 250;
        private const int cdt_termination_listener_timeout = 1000;
        private const byte cperform_diagnostic_test = 0xAA;
        private const byte cterminate_diagnostic_test = 0xFF;
        private const byte cdiagnostictest_finished_normally = 0xEE;
        private const byte cfuel_pump_dt = 0x80;
        private const int cdt_transmit_delay = 100;  // in ms
        private const byte cnonvalidated_input_errors = 0x10;
        private const byte cnonvalidated_output_errors = 0x11;
        private const byte cnonvalidated_functionality_errors = 0x12;
        private const byte cnonvalidated_input_errors_sign = 0x2B;
        private const byte cnonvalidated_output_errors_sign = 0x2C;
        private const byte cnonvalidated_functionality_errors_sign = 0x2D;
        private const byte cvalidated_input_errors = 0x14;
        private const byte cvalidated_output_errors = 0x15;
        private const byte cvalidated_functionality_errors = 0x16;
        private const byte cvalidated_input_errors_sign = 0x2E;
        private const byte cvalidated_output_errors_sign = 0x2F;
        private const byte cvalidated_functionality_errors_sign = 0x30;
        private const int ccheck_for_errors_timer_interval = 10000; // in ms thus 10s
        private const int capp_width = 1027;
        private const int capp_height = 763;
        private const SpPerfChart.TimerMode cdefaulttimermode = SpPerfChart.TimerMode.Simple;
        private const int cdefault_graph_spacing = 8;
        private const int cdefault_max_graph_entries = 1024;
        private const String cdebugautomaticallystart = "AutomaticallyStartDebug";
        private const String ccmssetmax = "Set max...";
        private const String ccmschangename = "Rename...";
        private const String ccmsreset = "Reset";
        private const String ccmsresetall = "Reset all splitters";
        private const String ccmschartmode = "Chart mode";
        private const String ccmschartmoderelative = "Relative";
        private const String ccmschartmodeabsolute = "Absolute";
        private const String ccmsavglineviewable = "Average line (viewable)";
        private const String ccmsavglinetotal = "Average line (all values)";
        private const String ccmshorizlines = "Horizontal grid lines";
        private const String ccmsvertlines = "Vertical grid lines";
        private const String ccmsupdatetime = "Update time interval";
        private const String ccmsgraphspacing = "Graph spacing";
        private const String ccmsmaxentries = "Max number of graph entries";
        private const String ccmscleargraph = "Clear";
        private const String ccmsstopgraph = "Stopped";
        private const String ccmsstartgraph = "Started";
        private const String ccmspausegraph = "Paused";
        private const String ccmsaddalarm = "Add alarm...";
        private const String ccmsdeletealarm = "Delete alarm...";
        private const String ccmsviewalarms = "View alarms...";
        private const String ccmsrenderalarmsetpoints = "Render alarm setpoints";
        private const String ccmstakepicture = "Take picture...";
        // for config file
        private const String ccfgsplitterhorizontal = "horizontalsplitter";
        private const int chorizontalsplitterdistance = 148;
        private const String ccfgsplittertopleft = "topleftsplitter";
        private const int ctopleftsplitterdistance = 245;
        private const String ccfgsplittertopright = "toprightsplitter";
        private const int ctoprightsplitterdistance = 246;
        private const String ccfgsplittertop = "topsplitter";
        private const int ctopsplitterdistance = 490;
        private const String ccfgsplitterbottomleft = "bottomleftsplitter";
        private const int cbottomleftsplitterdistance = 245;
        private const String ccfgsplitterbottomright = "bottomrightsplitter";
        private const int cbottomrightsplitterdistance = 246;
        private const String ccfgsplitterbottom = "bottomsplitter";
        private const int cbottomsplitterdistance = 490;
        private const String ccfgmaxbatteryvoltage = "maxbatteryvoltage";
        private const String ccfgmaxairtemp = "maxairtemp";
        private const String ccfgmaxwatertemp = "maxwatertemp";
        private const String ccfgmaxMAP = "maxMAP";
        private const String ccfgmaxthrottleangle = "maxthrottleangle";
        private const String ccfgmaxrpm = "maxrpm";
        private const String ccfgmaxinjectorduration = "maxinjectorduration";
        private const String ccfgmaxignitionadvance = "maxignitionadvance";
        private const String ccfgmasterpollenabled = "masterpollenabled";
        private const String ccfgpollgraphspeed = "pollgraphspeed";
        private const String ccfgcheckforecuerrors = "checkforecuerrors";
        private const String ccfgcheckforecuerrorstime = "checkforecuerrorstime";
        private const String ccfgappwidth = "appwidth";
        private const String ccfgappheight = "appheight";
        private const String ccfgappX = "appX";
        private const String ccfgappY = "appY";

        private const String ccfgbatteryvoltagename = "batteryvoltagename";
        private const String ccfgbatteryvoltageloggingenabled = "batteryvoltagelogging";
        private const String ccfgbatteryvoltagechartmode = "batteryvoltagechartmode";
        private const String ccfgbatteryvoltageavglineviewable = "batteryvoltageavglineviewable";
        private const String ccfgbatteryvoltageavglinetotal = "batteryvoltageavglinetotal";
        private const String ccfgbatteryvoltagehorizlines = "batteryvoltagehorizlines";
        private const String ccfgbatteryvoltagevertlines = "batteryvoltagevertlines";
        private const String ccfgbatteryvoltageupdatetime = "batteryvoltageupdatetime";
        private const String ccfgbatteryvoltagetimermode = "batteryvoltagetimermode";
        private const String ccfgbatteryvoltagegraphspacing = "batteryvoltagegraphspacing";
        private const String ccfgbatteryvoltagemaxentries = "batteryvoltagemaxentries";

        private const String ccfgairtempname = "airtempname";
        private const String ccfgairtemploggingenabled = "airtemplogging";
        private const String ccfgairtempchartmode = "airtempchartmode";
        private const String ccfgairtempavglineviewable = "airtempavglineviewable";
        private const String ccfgairtempavglinetotal = "airtempavglinetotal";
        private const String ccfgairtemphorizlines = "airtemphorizlines";
        private const String ccfgairtempvertlines = "airtempvertlines";
        private const String ccfgairtempupdatetime = "airtempupdatetime";
        private const String ccfgairtemptimermode = "airtemptimermode";
        private const String ccfgairtempgraphspacing = "airtempgraphspacing";
        private const String ccfgairtempmaxentries = "airtempmaxentries";

        private const String ccfgwatertempname = "watertempname";
        private const String ccfgwatertemploggingenabled = "watertemplogging";
        private const String ccfgwatertempchartmode = "watertempchartmode";
        private const String ccfgwatertempavglineviewable = "watertempavglineviewable";
        private const String ccfgwatertempavglinetotal = "watertempavglinetotal";
        private const String ccfgwatertemphorizlines = "watertemphorizlines";
        private const String ccfgwatertempvertlines = "watertempvertlines";
        private const String ccfgwatertempupdatetime = "watertempupdatetime";
        private const String ccfgwatertemptimermode = "watertemptimermode";
        private const String ccfgwatertempgraphspacing = "watertempgraphspacing";
        private const String ccfgwatertempmaxentries = "watertempmaxentries";

        private const String ccfgMAPname = "MAPname";
        private const String ccfgMAPloggingenabled = "MAPlogging";
        private const String ccfgMAPchartmode = "MAPchartmode";
        private const String ccfgMAPavglineviewable = "MAPavglineviewable";
        private const String ccfgMAPavglinetotal = "MAPavglinetotal";
        private const String ccfgMAPhorizlines = "MAPhorizlines";
        private const String ccfgMAPvertlines = "MAPvertlines";
        private const String ccfgMAPupdatetime = "MAPupdatetime";
        private const String ccfgMAPtimermode = "MAPtimermode";
        private const String ccfgMAPgraphspacing = "MAPgraphspacing";
        private const String ccfgMAPmaxentries = "MAPmaxentries";

        private const String ccfgthrottleanglename = "throttleanglename";
        private const String ccfgthrottleangleloggingenabled = "throttleanglelogging";
        private const String ccfgthrottleanglechartmode = "throttleanglechartmode";
        private const String ccfgthrottleangleavglineviewable = "throttleangleavglineviewable";
        private const String ccfgthrottleangleavglinetotal = "throttleangleavglinetotal";
        private const String ccfgthrottleanglehorizlines = "throttleanglehorizlines";
        private const String ccfgthrottleanglevertlines = "throttleanglevertlines";
        private const String ccfgthrottleangleupdatetime = "throttleangleupdatetime";
        private const String ccfgthrottleangletimermode = "throttleangletimermode";
        private const String ccfgthrottleanglegraphspacing = "throttleanglegraphspacing";
        private const String ccfgthrottleanglemaxentries = "throttleanglemaxentries";

        private const String ccfgRPMname = "RPMname";
        private const String ccfgRPMloggingenabled = "RPMlogging";
        private const String ccfgRPMchartmode = "RPMchartmode";
        private const String ccfgRPMavglineviewable = "RPMavglineviewable";
        private const String ccfgRPMavglinetotal = "RPMavglinetotal";
        private const String ccfgRPMhorizlines = "RPMhorizlines";
        private const String ccfgRPMvertlines = "RPMvertlines";
        private const String ccfgRPMupdatetime = "RPMupdatetime";
        private const String ccfgRPMtimermode = "RPMtimermode";
        private const String ccfgRPMgraphspacing = "RPMgraphspacing";
        private const String ccfgRPMmaxentries = "RPMmaxentries";

        private const String ccfginjectordurationname = "injectordurationname";
        private const String ccfginjectordurationloggingenabled = "injectordurationlogging";
        private const String ccfginjectordurationchartmode = "injectordurationchartmode";
        private const String ccfginjectordurationavglineviewable = "injectordurationavglineviewable";
        private const String ccfginjectordurationavglinetotal = "injectordurationavglinetotal";
        private const String ccfginjectordurationhorizlines = "injectordurationhorizlines";
        private const String ccfginjectordurationvertlines = "injectordurationvertlines";
        private const String ccfginjectordurationupdatetime = "injectordurationupdatetime";
        private const String ccfginjectordurationtimermode = "injectordurationtimermode";
        private const String ccfginjectordurationgraphspacing = "injectordurationgraphspacing";
        private const String ccfginjectordurationmaxentries = "injectordurationmaxentries";

        private const String ccfgignitionadvancename = "ignitionadvancename";
        private const String ccfgignitionadvanceloggingenabled = "ignitionadvancelogging";
        private const String ccfgignitionadvancechartmode = "ignitionadvancechartmode";
        private const String ccfgignitionadvanceavglineviewable = "ignitionadvanceavglineviewable";
        private const String ccfgignitionadvanceavglinetotal = "ignitionadvanceavglinetotal";
        private const String ccfgignitionadvancehorizlines = "ignitionadvancehorizlines";
        private const String ccfgignitionadvancevertlines = "ignitionadvancevertlines";
        private const String ccfgignitionadvanceupdatetime = "ignitionadvanceupdatetime";
        private const String ccfgignitionadvancetimermode = "ignitionadvancetimermode";
        private const String ccfgignitionadvancegraphspacing = "ignitionadvancegraphspacing";
        private const String ccfgignitionadvancemaxentries = "ignitionadvancemaxentries";
        #endregion

        #region Variables
        private bool loaded = false;
        private SerialComms serial_comms = null;
        private Thread poll_ecu_data = null;
        private bool abort_poll_ecu_data = false;
        private Thread update_values = null;
        private Thread dt_termination_listener = null;
        private Dialog dialog = null;
        private BackgroundWorker bwCommsInit = null;
        private bool form_closing = false;
#if DoGetAvailableMode1PIDs
        private List<Byte> available_MODE1_PIDs = null;
#endif
        private byte[] repair_code = null;
        //private int poll_ecu_data_loop_sleep = 0; // 250
        private List<Byte> bytes_received = null;

        // for charts - will be overriden by cfg file entries
        private float max_battery_voltage = 14;
        private float max_air_temp = 60;
        private float max_water_temp = 60;
        private float max_MAP = 750;
        private float max_throttle_angle = 115;
        private float max_rpm = 13000;
        private float max_injector_duration = 15;
        private float max_ignition_advance = 55;
        private List<SpPerfChart.PerfChart> perfcharts = null;
        private List<String> perfchartsnames = null;
        private List<SplitContainer> splitters = null;
        private List<int> splitter_defaults = null;
        private List<CheckBox> pccbs = null;
        private CultureInfo usculture = new CultureInfo("en-US");

        private float battery_voltage = 0.0f, pc_battery_voltage = 0.0f;
        private float throttle_angle = 0.0f, pc_throttle_angle = 0.0f;
        private float manifold_absolute_pressure = 0.0f, pc_manifold_absolute_pressure = 0.0f;
        private float air_temp = 0.0f, pc_air_temp = 0.0f;
        private float water_temp = 0.0f, pc_water_temp = 0.0f;
        private float trimmer_position = 0.0f;
        private float rpm = 0.0f, pc_rpm = 0.0f;
        private byte[] rpm_bytes = null;
        private Int16 irpm = 0;
        private float injector_duration = 0.0f, pc_injector_duration = 0.0f;
        private byte[] injector_duration_bytes = null;
        private Int16 iinjector_duration = 0;
        private float ignition_advance = 0.0f, pc_ignition_advance = 0.0f;
        private int fuel_trim = 0;
        private int fuel_trim_power_on = 0;
        private int start_enrichment = 0;
        private int spare1 = 0;
        private int spare2 = 0;
        private bool master_poll_enable = true;
        private CheckBox diagnostic_test_in_progress = null;
        private int diagnostic_test_result = 0;
        private byte byte_to_transmit = 0x0;
        private List<byte> DTcodes = null;
        private List<CheckBox> DTcbs = null;

        // TDW disable trims
        private BitArray tdwdisabletrims = null;
        private bool get_tdwdisabletrims = false;

        // TDW global trims
        private BitArray tdwglobaltrims = null;
        private bool get_tdwglobaltrims = false;

        // ECU flags
        private BitArray ecuflags = null;

        // TDW flags
        private BitArray tdwflags = null;
        private bool get_tdwflags = false;

        // FGSTAT
        private byte fgstat = 0;

        // for dumping EEPROM
        private bool dump_EEPROM = false;
        private int dump_EEPROM_byte_index = 0;
        private byte[] EEPROM_contents = null;

        // for dumping a byte
        private bool dump_Byte = false;

        // for outside windows to send/receive a byte
        private bool send_byte = false;
        private byte byte_to_send = 0;
        private bool byte_to_send_result = false;
        private bool byte_to_receive_expected = false;
        private byte byte_to_receive = 0;
        private bool byte_to_receive_result = false;

        //private byte[] current_spark_advance_word_bytes = null;
        //private int icurrent_spark_advance_word = 0;

        private HighPrecisionTimer hpt_poll_ecu_data = null;

        private bool check_for_ecu_errors_when_connected = true;
        private bool check_for_errors = false;
        // non-validated (RAM)
        private byte err_co_inp = 0x0;
        private byte err_co_out = 0x0;
        private byte err_co_fun = 0x0;
        // validated (EEPROM)
        private byte err_va_inp = 0x0;
        private byte err_va_out = 0x0;
        private byte err_va_fun = 0x0;
        // common to both non-validated and validated
        private List<String> err_co_va_inp_list = null;
        private List<String> err_co_va_out_list = null;
        private List<String> err_co_va_fun_list = null;
        // non-validated sign (RAM)
        private byte se_co_inp = 0x0;
        private byte se_co_out = 0x0;
        private byte se_co_fun = 0x0;
        // validated sign (EEPROM)
        private byte se_va_inp = 0x0;
        private byte se_va_out = 0x0;
        private byte se_va_fun = 0x0;
        // common to both non-validated and validated sign
        private List<String> se_co_va_inp_list = null;
        private List<String> se_co_va_out_list = null;
        private List<String> se_co_va_fun_list = null;

        private EEPROM frm_eeprom = null;

        // for debugging
        private Process dbgview = null;

        // for config file
        private String cfgfilepath = "";
        private Dictionary<String, List<String>> cfgfileentries = new Dictionary<string, List<string>>();

        // for playing sounds
        private Device device = null;
        #endregion

        #region Static variables
        internal static String soundfolderpath = "";
        internal static String picturesfolderpath = "";
        #endregion

        #region Properties
        #region TDWGlobalTrims
        internal BitArray TDWGlobalTrims
        {
            get
            {
                if (serial_comms.ECU_Initialized != DialogResult.Yes)
                {
                    return tdwglobaltrims;
                }

                get_tdwglobaltrims = true;
                while (get_tdwglobaltrims)
                {
                    Application.DoEvents();
                }

                return tdwglobaltrims;
            }
        }
        #endregion

        #region TDWGlobalFuelTrimValue
        internal byte TDWGlobalFuelTrimValue
        {
            get
            {
                byte_to_receive_expected = true;
                byte_to_send = cGlobalFuelTrimGet;
                send_byte = true;
                while (send_byte)
                {
                    Application.DoEvents();
                }

                if (!byte_to_send_result || !byte_to_receive_result)
                {
                    return 0x80;
                }

                return byte_to_receive;
            }
        }
        #endregion

        #region TDWGlobalVertCylTrimValue
        internal byte TDWGlobalVertCylTrimValue
        {
            get
            {
                byte_to_receive_expected = true;
                byte_to_send = cGlobalVertCylTrimGet;
                send_byte = true;
                while (send_byte)
                {
                    Application.DoEvents();
                }

                if (!byte_to_send_result || !byte_to_receive_result)
                {
                    return 0x80;
                }

                return byte_to_receive;
            }
        }
        #endregion

        #region TDWGlobalSparkTrimValue
        internal byte TDWGlobalSparkTrimValue
        {
            get
            {
                byte_to_receive_expected = true;
                byte_to_send = cGlobalSparkTrimGet;
                send_byte = true;
                while (send_byte)
                {
                    Application.DoEvents();
                }

                if (!byte_to_send_result || !byte_to_receive_result)
                {
                    return 0x80;
                }

                return byte_to_receive;
            }
        }
        #endregion

        #region TransmitByteResult
        internal bool TransmitByteResult
        {
            get
            {
                return byte_to_send_result;
            }
        }
        #endregion

        #region ReceivedByteResult
        internal bool ReceivedByteResult
        {
            get
            {
                return byte_to_receive_result;
            }
        }
        #endregion

        #region ReceivedByte
        internal byte ReceivedByte
        {
            get
            {
                return byte_to_receive;
            }
        }
        #endregion

        #region ReceivedByteExpected
        internal bool ReceivedByteExpected
        {
            get
            {
                return byte_to_receive_expected;
            }
            set
            {
                byte_to_receive_expected = value;
            }
        }
        #endregion

        #region TDWDisableTrims
        internal BitArray TDWDisableTrims
        {
            get
            {
                if (serial_comms.ECU_Initialized != DialogResult.Yes)
                {
                    return tdwdisabletrims;
                }

                get_tdwdisabletrims = true;
                while (get_tdwdisabletrims)
                {
                    Application.DoEvents();
                }

                return tdwdisabletrims;
            }
        }
        #endregion
        #endregion

        #region Contructor
        public WM16MEV()
        {
            // being lazy on this one!
            CheckForIllegalCrossThreadCalls = false;
            InitializeComponent();
            rtbStatus.Clear();

            serial_comms = new SerialComms();
            serial_comms.OnReceiveData += ReceivingData;
            serial_comms.OnSendData += SendingData;
            SerialComms.ShowMessage += DisplayMessage;

            dialog = new Dialog();
            dialog.lblMessage.Text = "";

            tsslIsoCode.Text = ciso_code;
            tsslECUInitialized.Text = cecu_initialized + (serial_comms.ECU_Initialized == DialogResult.Yes ? "Yes" : "No");
            tsslRepairCode.Text = crepair_code;

            UpdateAvailableCommPorts();

            //SetPCTimerMode(SpPerfChart.TimerMode.Disabled);

            hpt_poll_ecu_data = new HighPrecisionTimer();
            hpt_poll_ecu_data.Timer(HighPrecisionTimer.HPT.Stop);
            //poll_ecu_data_loop_sleep = 0;

            diagnostic_test_in_progress = null;
            EEPROM_contents = null;
            tdwdisabletrims = new BitArray(8, false);
            get_tdwdisabletrims = false;
            tdwglobaltrims = new BitArray(8, false);
            get_tdwglobaltrims = false;
            ecuflags = new BitArray(8, false);
            tdwflags = new BitArray(8, false);
            get_tdwflags = false;

            DTcbs = new List<CheckBox>() { cbDTFuelPump, cbDTHorizontalInjectorTest, cbDTCoil1, cbDTCoil2, cbDTClearErrors, null, cbDTTach, cbDTVerticalInjector };
            foreach (CheckBox cb in DTcbs)
            {
                if (cb != null)
                {
                    cb.Enabled = false;
                }
            }

            DTcodes = new List<byte>() { 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87 };

            err_co_fun = 0x0;
            err_co_inp = 0x0;
            err_co_out = 0x0;
            err_va_fun = 0x0;
            err_va_inp = 0x0;
            err_va_out = 0x0;
            err_co_va_inp_list = new List<string>() { "Throttle Position Sensor", "Barometric pressure Sensor", "Lambda Probe (KO2 outside range)", "Water Temperature Sensor", "Air Temperature Sensor", "Battery Voltage", "Estimate Error Steps (NU)", "Not used" };
            err_co_va_out_list = new List<string>() { "Injector", "Ignition Coil 1", "Ignition Coil 2", "ICV", "Charcoal Canister", "Air Conditioner", "Fuel Pump", "Relay (NU)" };
            err_co_va_fun_list = new List<string>() { "Calibration", "RAM", "EPROM (ROM)", "EEPROM", "uC (Microcontroller)", "RPM / TDC Sensor", "Not used", "Not used" };
            se_co_fun = 0x0;
            se_co_inp = 0x0;
            se_co_out = 0x0;
            se_va_fun = 0x0;
            se_va_inp = 0x0;
            se_va_out = 0x0;
            // for the 3 below each error can have 1 of 2 possible signs - 0 or 1. Value 1 is listed first then value 0.
            se_co_va_inp_list = new List<string>() { "Throttle Position Sensor shorted to Gnd", "Throttle Position Sensor shorted to Vbatt", "Barometric pressure Sensor shorted to Vbatt", "Barometric pressure Sensor shorted to Gnd", "Not used", "Not used", "Water Temperature Sensor shorted to Gnd", "Water Temperature Sensor shorted to Vbatt", "Air Temperature Sensor shorted to Gnd", "Air Temperature Sensor shorted to Vbatt", "Vbatt larger than upper threshold", "Vbatt lower than lower threshold", "Not used", "Not used", "Not used", "Not used" };
            se_co_va_out_list = new List<string>() { "Injector Short circuited", "Injector Open circuited", "Coil 1 Shorted to Vbatt", "Coil 1 Shorted to Gnd", "Coil 2 Shorted to Vbatt", "Coil 1 Shorted to Gnd", "ICV Stepper Motor Short circuited", "ICV Stepper Motor Open Circuited", "Charcoal Canister shorted to Vbatt", "Charcoal Canister shorted to Gnd", "Air Conditioner shorted to Vbatt", "Air Conditioner shorted to Gnd", "Fuel pump shorted to Vbatt", "Fuel pump shorted to Gnd", "Generic Relay shorted to Vbatt", "Generic Relay shorted to Gnd" };
            se_co_va_fun_list = new List<string>() { "Calibration Parameters too Rich", "Calibration Parameters too Lean", "Not used", "Not used", "Not used", "Not used", "Not used", "Not used", "Not used", "Not used", "Not used", "Not used", "Not used", "Not used", "Not used", "Not used" };

            check_for_errors = false;

            splitters = new List<SplitContainer>() { scGraphsBottom, scGraphsBottomLeft, scGraphsBottomRight, scGraphsTop, scGraphsTopLeft, scGraphsTopRight, scGraphsHorizontal };
            splitter_defaults = new List<int>() { cbottomsplitterdistance, cbottomleftsplitterdistance, cbottomrightsplitterdistance, ctopsplitterdistance, ctopleftsplitterdistance, ctoprightsplitterdistance, chorizontalsplitterdistance };
            SetSplittersCMSs();

            soundfolderpath = Application.StartupPath + "\\" + csoundsfoldername;
            if (!Directory.Exists(soundfolderpath))
            {
                Directory.CreateDirectory(soundfolderpath);
            }

            picturesfolderpath = Application.StartupPath + "\\" + cpicturesfoldername;
            if (!Directory.Exists(picturesfolderpath))
            {
                Directory.CreateDirectory(picturesfolderpath);
            }
        }
        #endregion

        #region Form load
        private void WM16MEV_Load(object sender, EventArgs e)
        {
            this.Visible = false;

            ReadCfgFile();

            perfcharts = new List<SpPerfChart.PerfChart>() { pcAirTemp, pcBatteryVoltage, pcIgnitionAdvance, pcInjectorDuration, pcMAP, pcRPM, pcThrottleAngle, pcWaterTemp };
            perfchartsnames = new List<string>() { GetCfgFileEntry(ccfgairtempname)[0], GetCfgFileEntry(ccfgbatteryvoltagename)[0], GetCfgFileEntry(ccfgignitionadvancename)[0], GetCfgFileEntry(ccfginjectordurationname)[0], GetCfgFileEntry(ccfgMAPname)[0], GetCfgFileEntry(ccfgRPMname)[0], GetCfgFileEntry(ccfgthrottleanglename)[0], GetCfgFileEntry(ccfgwatertempname)[0] };
            for (int i = 0; i < perfcharts.Count; i++)
            {
                perfcharts[i].Tag = perfchartsnames[i];
            }
            pccbs = new List<CheckBox>() { cbAirTemp, cbBatteryVoltage, cbIgnitionAdvance, cbInjectorDuration, cbMAP, cbRPM, cbThrottleAngle, cbWaterTemp, cbFuelTrimPowerOn, cbFuelTrimRaw, cbSpare1, cbSpare2, cbStartEnrichment, cbTPSTrim };

            //SetPCsEnabledDisabled(false);

            SetPCCMSs();

            UpdateFormFromCfgFile();

            foreach (SpPerfChart.PerfChart pc in perfcharts)
            {
                toolTip1.SetToolTip(pc, toolTip1.GetToolTip(pc) + "\r\n\r\nRight click for options\r\n\r\nUpper left V = max visible value\r\nUpper left A = max value of all values\r\nBottom left V = min visible value\r\nBottom left A = min value of all values\r\nTop center V = average value of the visible values\r\nTop center A = average value of all values");
                pc.NewAlarm += new SpPerfChart.PerfChart.RaiseAlarm(pc_NewAlarm);
                pc.AlarmCleared += new SpPerfChart.PerfChart.RaiseAlarm(pc_AlarmCleared);
                pc.AlarmCompleted += new SpPerfChart.PerfChart.ClearAlarm(pc_AlarmCompleted);
                pc.AlarmDeleted += new SpPerfChart.PerfChart.ClearAlarm(pc_AlarmDeleted);
            }

            foreach (SplitContainer sc in splitters)
            {
                toolTip1.SetToolTip(sc, "Drag to resize slider\r\n\r\nRight click for options");
            }

            // create the DirectSound device
            device = new Device();
            device.SetCooperativeLevel(this, Microsoft.DirectX.DirectSound.CooperativeLevel.Normal);

            // for testing the sound device
            //PlaySound(Application.StartupPath + "\\AlarmClock.wav", 1.0f);

#if !Debugging_Enabled
            tsmiDebug.Visible = false;
            tsmiDebug.Enabled = false;
#else
            tsmiDebug.Enabled = File.Exists(Application.StartupPath + "\\Dbgview.exe");
#endif

            this.Text = GetAppString();
            DisplayMessage(GetAppString(), true, true);

            this.Visible = true;
        }
        #endregion

        #region Form shown
        private void WM16MEV_Shown(object sender, EventArgs e)
        {
            MessageBox.Show("Ensure ignition switch is in the OFF position!", "Start-up", MessageBoxButtons.OK, MessageBoxIcon.Information);

            //frm_eeprom = new EEPROM();
            //frm_eeprom.Show();
        }
        #endregion

        #region Form resize end
        private void WM16MEV_ResizeEnd(object sender, EventArgs e)
        {
            cfgfileentries[ccfgappwidth][0] = this.Width.ToString();
            cfgfileentries[ccfgappheight][0] = this.Height.ToString();
        }
        #endregion

        #region Form location changed
        private void WM16MEV_LocationChanged(object sender, EventArgs e)
        {
            try
            {
                cfgfileentries[ccfgappX][0] = this.Location.X.ToString();
                cfgfileentries[ccfgappY][0] = this.Location.Y.ToString();
            }
            catch
            {
                // do nothing - constructor will call this during initialization
            }
        }
        #endregion

        #region Form closing
        private void WM16MEV_FormClosing(object sender, FormClosingEventArgs e)
        {
            form_closing = true;

            // close the Dbgview app
            if (dbgview != null)
            {
                tsmiDebugStop_Click(null, null);
            }

            SerialComms.ShowMessage -= DisplayMessage;
            if (e.CloseReason == CloseReason.UserClosing)
            {
                DisplayMessage("User asked form to close...", true, true);
            }
            else
            {
                DisplayMessage("Closing form...", true, true);
            }

            // update the cfg file
            WriteCfgFile();

            if (clbAvailable_Comm_Ports.SelectedIndex > -1)
            {
                clbAvailable_Comm_Ports.SetItemChecked(clbAvailable_Comm_Ports.SelectedIndex, false);
            }

            if (serial_comms != null)
            {
                serial_comms.OnReceiveData -= ReceivingData;
                serial_comms.OnSendData -= SendingData;
                serial_comms.Abort();
            }

            if (bwCommsInit != null)
            {
                bwCommsInit.CancelAsync();
                try
                {
                    while (bwCommsInit.IsBusy)
                    {
                        Application.DoEvents();
                    }
                    bwCommsInit.Dispose();
                    bwCommsInit = null;
                }
                catch
                {
                    // do nothing
                }
            }

            if (poll_ecu_data != null)
            {
                poll_ecu_data.Abort();
                while (poll_ecu_data.IsAlive)
                {
                    Application.DoEvents();
                }
                poll_ecu_data = null;
            }

            if (update_values != null)
            {
                update_values.Abort();
                while (update_values.IsAlive)
                {
                    Application.DoEvents();
                }
                update_values = null;
            }

            if (dt_termination_listener != null)
            {
                dt_termination_listener.Abort();
                while (dt_termination_listener.IsAlive)
                {
                    Application.DoEvents();
                }
                dt_termination_listener = null;
            }

            if (serial_comms != null)
            {
                serial_comms.Destroy();
                serial_comms = null;
            }

            Destroy();
        }
        #endregion

        #region Form closed
        private void WM16MEV_FormClosed(object sender, FormClosedEventArgs e)
        {
            DisplayMessage("Exiting...", true, true);
        }
        #endregion

        #region GetAppString
        private string GetAppString()
        {
            return capp + " v" + GetEXEVersion();
        }
        #endregion

        #region File
        #region Exit
        private void tsmiFileExit_Click(object sender, EventArgs e)
        {
            this.Close();
        }
        #endregion
        #endregion

        #region Edit
        #region Clear history
        private void tsmiEditClearHistory_Click(object sender, EventArgs e)
        {
            rtbStatus.Clear();
        }
        #endregion
        #endregion

        #region Graphs
        #region Pause all
        private void tsmiGraphsPauseAll_Click(object sender, EventArgs e)
        {
            foreach (SpPerfChart.PerfChart pc in perfcharts)
            {
                pc.PerfChartStyle.Paused = true;
            }
        }
        #endregion

        #region Unpause all
        private void tsmiGraphsUnpauseAll_Click(object sender, EventArgs e)
        {
            foreach (SpPerfChart.PerfChart pc in perfcharts)
            {
                pc.PerfChartStyle.Paused = false;
            }
        }
        #endregion

        #region Clear all
        private void tsmiGraphsClearAll_Click(object sender, EventArgs e)
        {
            foreach (SpPerfChart.PerfChart pc in perfcharts)
            {
                pc.Clear();
            }
        }
        #endregion

        #region Start all
        private void tsmiGraphsStartAll_Click(object sender, EventArgs e)
        {
            foreach (SpPerfChart.PerfChart pc in perfcharts)
            {
                CheckBox cb = pccbs[perfcharts.IndexOf(pc)];
                cb.Checked = true;
            }
        }
        #endregion

        #region Stop all
        private void tsmiGraphsStopAll_Click(object sender, EventArgs e)
        {
            foreach (SpPerfChart.PerfChart pc in perfcharts)
            {
                CheckBox cb = pccbs[perfcharts.IndexOf(pc)];
                cb.Checked = false;
            }
        }
        #endregion

        #region Alarms
        #region Render all alarm setpoints (Master)
        private void tsmiGraphsAlarmsRenderAllAlarmSetpointsMaster_Click(object sender, EventArgs e)
        {
            foreach (SpPerfChart.PerfChart pc in perfcharts)
            {
                foreach (ToolStripItem tsi in pc.ContextMenuStrip.Items)
                {
                    if (tsi is ToolStripMenuItem)
                    {
                        if (tsi.Text == ccmsrenderalarmsetpoints)
                        {
                            PCCMS_Click((ToolStripMenuItem)tsi, new EventArgs());
                            break;
                        }
                    }
                }
            }
        }
        #endregion

        #region Show all alarm setpoints
        private void tsmiGraphsAlarmsShowAllAlarmSetpoints_Click(object sender, EventArgs e)
        {
            foreach (SpPerfChart.PerfChart pc in perfcharts)
            {
                pc.ShowAllAlarmSetpoints();
            }
        }
        #endregion

        #region Hide all alarm setpoints
        private void tsmiGraphsAlarmsHideAllAlarmSetpoints_Click(object sender, EventArgs e)
        {
            foreach (SpPerfChart.PerfChart pc in perfcharts)
            {
                pc.HideAllAlarmSetpoints();
            }
        }
        #endregion

        #region Delete all alarms
        private void tsmiGraphsAlarmsDeleteAllAlarms_Click(object sender, EventArgs e)
        {
            foreach (SpPerfChart.PerfChart pc in perfcharts)
            {
                pc.RemoveAllAlarms();
            }
        }
        #endregion
        #endregion
        #endregion

        #region ECU
        #region Drop down opening
        private void tsmiECU_DropDownOpening(object sender, EventArgs e)
        {
            tsmiECUDump.Enabled = (!dump_EEPROM && (serial_comms.ECU_Initialized == DialogResult.Yes) && ecuflags[cecuflags_supports_memory_dumping]);
            tsmiECUShowECUFlags.Enabled = (serial_comms.ECU_Initialized == DialogResult.Yes);
            tsmiECUShowFGSTATFlags.Enabled = (serial_comms.ECU_Initialized == DialogResult.Yes);
            tsmiECUTDW.Enabled = ((serial_comms.ECU_Initialized == DialogResult.Yes) && ecuflags[cecuflags_supports_tdwflags]);
            tsmiECUGlobalTrim.Enabled = ((serial_comms.ECU_Initialized == DialogResult.Yes) && ecuflags[cecuflags_supports_global_trims]);
            tsmiECUDisableTrim.Enabled = ((serial_comms.ECU_Initialized == DialogResult.Yes) && ecuflags[cecuflags_supports_disable_trims]);
        }
        #endregion

        #region Errors
        #region Drop down opening
        private void tsmiECUErrors_DropDownOpening(object sender, EventArgs e)
        {
            tsmiECUErrorsCheckForECUErrors.Enabled = (serial_comms.ECU_Initialized == DialogResult.Yes);
        }
        #endregion

        #region Check for ECU errors at init
        private void tsmiECUErrorsCheckForECUErrorsAtInit_Click(object sender, EventArgs e)
        {
            check_for_ecu_errors_when_connected = !check_for_ecu_errors_when_connected;
            tsmiECUErrorsCheckForECUErrorsAtInit.Checked = check_for_ecu_errors_when_connected;
            tsmiECUErrorsCheckForECUErrorsAtInit.ToolTipText = "When checked the ECU will be interrogated for any errors present " + check_for_errors_timer.Interval.ToString() + "ms after it has been initialized";
            cfgfileentries[ccfgcheckforecuerrors][0] = check_for_ecu_errors_when_connected.ToString();
        }
        #endregion

        #region Check for ECU errors
        private void tsmiECUErrorsCheckForECUErrors_Click(object sender, EventArgs e)
        {
            if (check_for_errors_timer.Enabled)
            {
                DisplayMessage("Checking for ECU errors already initiated!", true, true);
                return;
            }

            check_for_errors = true;
        }
        #endregion
        #endregion

        #region Dump
        #region EEPROM
        private void tsmiECUDumpEEPROM_Click(object sender, EventArgs e)
        {
            // check to see if EEPROM is already being dumped
            if (dump_EEPROM)
            {
                MessageBox.Show("EEPROM is currently being dumped", "Active", MessageBoxButtons.OK);
                return;
            }

            DialogResult dr = DialogResult.Cancel;

            if (EEPROM_contents != null)
            {
                dr = MessageBox.Show("EEPROM has been previously dumped. Save the contents of this dump?", "Dump EEPROM", MessageBoxButtons.YesNoCancel);
                if (dr == DialogResult.Cancel)
                {
                    return;
                }
                else if (dr == DialogResult.No)
                {
                    EEPROM_contents = null;
                    dump_EEPROM_byte_index = -1;
                }
            }

            if (dr != DialogResult.Yes)
            {
                dump_EEPROM = true;

                DisplayDialog("Dumping EEPROM...", true);

                while (dump_EEPROM)
                {
                    Application.DoEvents();
                }

                DisplayDialog("", false);
                Application.DoEvents();

                if (EEPROM_contents == null)
                {
                    return;
                }
            }

            FileStream fs = null;
            BinaryWriter bw = null;
            SaveFileDialog sfd = null;

            try
            {
                sfd = new SaveFileDialog();
                sfd.AddExtension = true;
                sfd.AutoUpgradeEnabled = false;
                sfd.CheckFileExists = false;
                sfd.CheckPathExists = true;
                sfd.CreatePrompt = false;
                sfd.DefaultExt = "hex";
                sfd.Filter = "HEX files|*.hex";
                sfd.FilterIndex = 0;
                sfd.InitialDirectory = "";
                sfd.OverwritePrompt = true;
                sfd.RestoreDirectory = true;
                sfd.ShowHelp = false;
                sfd.SupportMultiDottedExtensions = false;
                sfd.Title = "Save EEPROM dump as...";
                sfd.ValidateNames = true;
                dr = sfd.ShowDialog();
                if (dr != DialogResult.OK || sfd.FileName == null || sfd.FileName == "")
                {
                    return;
                }

                try
                {
                    fs = new FileStream(sfd.FileName, FileMode.Create, FileAccess.Write);
                }
                catch (Exception ed)
                {
                    DisplayMessage("Exception while trying to create EEPROM dump file " + sfd.FileName + ".\r\nException is " + ed.Message, true, true);
                    return;
                }

                bw = new BinaryWriter(fs);

                foreach (Byte b in EEPROM_contents)
                {
                    bw.Write(b);
                }

                bw.Close();

                MessageBox.Show(sfd.FileName + " created successfully!", "Dump EEPROM", MessageBoxButtons.OK);
            }
            finally
            {
                if (sfd != null)
                {
                    sfd.Dispose();
                    sfd = null;
                }

                if (bw != null)
                {
                    bw = null;
                }

                if (fs != null)
                {
                    try
                    {
                        fs.Close();
                    }
                    catch
                    { }
                    fs.Dispose();
                    fs = null;
                }
            }

        }
        #endregion

        #region Byte
        private void tsmiECUDumpByte_Click(object sender, EventArgs e)
        {
            DumpByte db = null;

            try
            {
                db = new DumpByte(this);
                db.ShowDialog();
            }
            finally
            {
                if (db != null)
                {
                    db.Destroy();
                    db.Dispose();
                    db = null;
                }
            }
        }
        #endregion
        #endregion

        #region TDW disable trim flags
        private void tsmiECUDisableTrim_Click(object sender, EventArgs e)
        {
            DisplayDialog("Reading disable trims...", true);

            DisableTrims dt = null;

            try
            {
                get_tdwglobaltrims = true;
                while (get_tdwglobaltrims)
                {
                    Application.DoEvents();
                }

                String mes = "TDW disable trims:\r\n\r\n";
                mes += "Disable trims enabled: " + tdwdisabletrims[ctdwdisabletrims_enabled].ToString() + "\r\n";
                mes += "Disable fuel AirTemp trim enabled: " + tdwdisabletrims[ctdwdisabletrims_fuel_airtemp_enabled].ToString() + "\r\n";
                mes += "Disable fuel WaterTemp trim enabled: " + tdwdisabletrims[ctdwdisabletrims_fuel_watertemp_enabled].ToString() + "\r\n";
                mes += "Disable fuel Altitude trim enabled: " + tdwdisabletrims[ctdwdisabletrims_fuel_altitude_enabled].ToString() + "\r\n";

                DisplayMessage(mes, false, true);
                //MessageBox.Show(mes, "TDW disable trims", MessageBoxButtons.OK);

                try
                {
                    dt = new DisableTrims(this);
                    DisplayDialog("", false);
                    dt.ShowDialog();
                }
                finally
                {
                    DisplayDialog("", false);
                }
            }
            finally
            {
                DisplayDialog("", false);

                if (dt != null)
                {
                    dt.Destroy();
                    dt.Dispose();
                    dt = null;
                }
            }
        }
        #endregion

        #region TDW global trim flags
        private void tsmiECUGlobalTrim_Click(object sender, EventArgs e)
        {
            DisplayDialog("Reading global trims...", true);

            GlobalTrims gt = null;
            
            try
            {
                get_tdwglobaltrims = true;
                while (get_tdwglobaltrims)
                {
                    Application.DoEvents();
                }

                String mes = "TDW global trims:\r\n\r\n";
                mes += "Global trims initialized: " + tdwglobaltrims[ctdwglobaltrims_initialized].ToString() + "\r\n";
                mes += "Global fuel trim enabled: " + tdwglobaltrims[ctdwglobaltrims_fuel_enabled].ToString() + "\r\n";
                mes += "Global spark trim enabled: " + tdwglobaltrims[ctdwglobaltrims_spark_enabled].ToString() + "\r\n";
                mes += "Global vertical cylinder trim enabled: " + tdwglobaltrims[ctdwglobaltrims_vertcyl_enabled].ToString() + "\r\n";

                DisplayMessage(mes, false, true);
                //MessageBox.Show(mes, "TDW global trims", MessageBoxButtons.OK);

                try
                {
                    gt = new GlobalTrims(this);
                    DisplayDialog("", false);
                    gt.ShowDialog();
                }
                finally
                {
                    DisplayDialog("", false);
                }
            }
            finally
            {
                DisplayDialog("", false);

                if (gt != null)
                {
                    gt.Destroy();
                    gt.Dispose();
                    gt = null;
                }
            }
        }
        #endregion

        #region TDW flags
        private void tsmiECUTDWDisplay_Click(object sender, EventArgs e)
        {
            DisplayDialog("Reading TDW flags...", true);
            try
            {
                get_tdwflags = true;
                while (get_tdwflags)
                {
                    Application.DoEvents();
                }
            }
            finally
            {
                DisplayDialog("", false);
            }

            String mes = "TDW flags:\r\n\r\n";
            mes += "Power-up tach performed: " + tdwflags[ctdwflags_powerup_tach_performed].ToString() + "\r\n";
            mes += "Can dump memory: " + tdwflags[ctdwflags_can_dump_memory].ToString() + "\r\n";
            mes += "Dumping memory: " + tdwflags[ctdwflags_dumping_memory].ToString() + "\r\n";
            mes += "Dumping byte: " + tdwflags[ctdwflags_dumping_byte].ToString() + "\r\n";

            DisplayMessage(mes, false, true);
            MessageBox.Show(mes, "TDW flags", MessageBoxButtons.OK);
        }
        #endregion

        #region Show ECU flags
        private void tsmiECUShowECUFlags_Click(object sender, EventArgs e)
        {
            String mes = "ECU flags:\r\n\r\n";
            mes += "Supports TDW flags: " + ecuflags[cecuflags_supports_tdwflags].ToString() + "\r\n";
            mes += "Supports memory dumping: " + ecuflags[cecuflags_supports_memory_dumping].ToString() + "\r\n";
            mes += "Supports global trims: " + ecuflags[cecuflags_supports_global_trims].ToString() + "\r\n";
            mes += "Supports disable trims: " + ecuflags[cecuflags_supports_disable_trims].ToString() + "\r\n";
            mes += "Supports byte dump: " + ecuflags[cecuflags_supports_byte_dump].ToString() + "\r\n";
            mes += "Supports high speed comms: " + ecuflags[cecuflags_supports_high_speed_comms].ToString() + "\r\n";

            DisplayMessage(mes, false, true);
            MessageBox.Show(mes, "ECU flags", MessageBoxButtons.OK);
        }
        #endregion

        #region Show FGSTAT flags
        private void tsmiECUShowFGSTATFlags_Click(object sender, EventArgs e)
        {
            String mes = "FGSTAT flags:\r\n\r\n";
            if ((fgstat & (byte)Math.Pow(2, 0)) == 1)
            {
                mes += "Diagnostics OK, stepper in Run and water temp OK\r\n";
            }
            if ((fgstat & (byte)Math.Pow(2, 1)) == 1)
            {
                mes += "Motor rotating\r\n";
            }
            if ((fgstat & (byte)Math.Pow(2, 2)) == 1)
            {
                mes += "Square Wave Signal OK (crank trigger)\r\n";
            }
            if ((fgstat & (byte)Math.Pow(2, 3)) == 1)
            {
                mes += "throttle angle at minimum or maximum opening\r\n";
            }
            if ((fgstat & (byte)Math.Pow(2, 4)) == 1)
            {
                mes += "O2 - Operating in Closed Loop\r\n";
            }
            if ((fgstat & (byte)Math.Pow(2, 5)) == 1)
            {
                mes += "Air Conditionion on - always on on a motorcycle!\r\n";
            }
            if ((fgstat & (byte)Math.Pow(2, 6)) == 1)
            {
                mes += "Autotrim - System Calibration OK\r\n";
            }
            if ((fgstat & (byte)Math.Pow(2,7)) == 1)
            {
                mes += "execution of stepper test is active - Testing Stepper Motor\r\n";
            }

            DisplayMessage(mes, false, true);
            MessageBox.Show(mes, "FGSTAT flags", MessageBoxButtons.OK);
        }
        #endregion
        #endregion

        #region Debug
        #region Drop down opening
        private void tsmiDebug_DropDownOpening(object sender, EventArgs e)
        {
            if (!File.Exists(Application.StartupPath + "\\Dbgview.exe"))
            {
                tsmiDebugStart.Enabled = false;
                tsmiDebugStop.Enabled = (dbgview == null);
                return;
            }
        }
        #endregion

        #region tsmiDebugStart_Click
        private void tsmiDebugStart_Click(object sender, EventArgs e)
        {
            if (dbgview != null)
            {
                tsmiDebugStart.Enabled = false;
                tsmiDebugStop.Enabled = true;
                return;
            }

            ProcessStartInfo psi = new ProcessStartInfo(Application.StartupPath + "\\Dbgview.exe");
            psi.WindowStyle = ProcessWindowStyle.Minimized;
            dbgview = Process.Start(psi);
            dbgview.Exited += new EventHandler(dbgview_Exited);
            dbgview.EnableRaisingEvents = true;
            tsmiDebugStart.Enabled = !(dbgview != null);
            tsmiDebugStop.Enabled = (dbgview != null);
        }
        #endregion

        #region tsmiDebugStop_Click
        private void tsmiDebugStop_Click(object sender, EventArgs e)
        {
            if (dbgview == null)
            {
                tsmiDebugStart.Enabled = true;
                tsmiDebugStop.Enabled = false;

                tsmiDebug_DropDownOpening(null, null);
                return;
            }

            if (dbgview.HasExited)
            {
                dbgview_Exited(null, null);
            }
            else
            {
                try
                {
                    dbgview.Kill();
                }
                catch { }
            }
        }
        #endregion

        #region tsmiDebugAutomaticallyStart_Click
        private void tsmiDebugAutomaticallyStart_Click(object sender, EventArgs e)
        {
            tsmiDebugAutomaticallyStart.Checked = !tsmiDebugAutomaticallyStart.Checked;
            cfgfileentries[cdebugautomaticallystart][0] = tsmiDebugAutomaticallyStart.Checked.ToString();
        }
        #endregion

        #region DbgView exited
        void dbgview_Exited(object sender, EventArgs e)
        {
            if (dbgview != null)
            {
                dbgview.Dispose();
                dbgview = null;
            }

            tsmiDebugStart.Enabled = true;
            tsmiDebugStop.Enabled = false;

            tsmiDebug_DropDownOpening(null, null);
        }
        #endregion
        #endregion

        #region Update available comm ports
        private void UpdateAvailableCommPorts()
        {
            clbAvailable_Comm_Ports.Items.Clear();
            SerialComms.GetAvailableCommPorts(false);

            foreach (String s in SerialComms.Available_Comm_Ports)
            {
                clbAvailable_Comm_Ports.Items.Add(s);
            }
        }
        #endregion

        #region btnSelect_Comm_Port_Click
        private void btnSelect_Comm_Port_Click(object sender, EventArgs e)
        {
            clbAvailable_Comm_Ports.SelectedIndex = -1;
            UpdateAvailableCommPorts();
        }
        #endregion

        #region clbAvailable_Comm_Ports_MouseDoubleClick
        private void clbAvailable_Comm_Ports_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            if (clbAvailable_Comm_Ports.SelectedIndex >= 0)
            {
                clbAvailable_Comm_Ports.SetItemChecked(clbAvailable_Comm_Ports.SelectedIndex, !clbAvailable_Comm_Ports.GetItemChecked(clbAvailable_Comm_Ports.SelectedIndex));
            }
        }
        #endregion

        #region clbAvailable_Comm_Ports_ItemCheck
        private void clbAvailable_Comm_Ports_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            String comm_port_name = (String)clbAvailable_Comm_Ports.Items[e.Index];
            Trace.WriteLine(comm_port_name + " selected");

            check_for_errors_timer.Stop();
            check_for_errors_timer.Tick -= CheckForErrorsTimerTick;
            check_for_errors = false;

            if (e.CurrentValue == CheckState.Unchecked)
            {
                abort_poll_ecu_data = false;

                tsslIsoCode.Text = ciso_code;
                tsslECUInitialized.Text = cecu_initialized + "No";
                tsslRepairCode.Text = crepair_code;

                bwCommsInit = new BackgroundWorker();
                bwCommsInit.WorkerReportsProgress = true;
                bwCommsInit.WorkerSupportsCancellation = true;
                bwCommsInit.DoWork += new DoWorkEventHandler(bwCommsInit_DoWork);
                bwCommsInit.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwCommsInit_RunWorkerCompleted);

                Trace.WriteLine("Turn ignition switch to ON position");
                DisplayDialog("Turn ignition switch to ON position", true);

                bwCommsInit.RunWorkerAsync(comm_port_name);
            }
            else if (e.CurrentValue == CheckState.Checked)
            {
                //SetPCsEnabledDisabled(false);

                if (diagnostic_test_in_progress != null)
                {
                    /*
                    if (Transmit(0xFF))
                    {
                        List<byte> bytes_received = Receive(1, cPID_receive_timeout);
                        if (bytes_received == null)
                        {
                            MessageBox.Show("Failed to terminate diagnostic test in progress", "Error", MessageBoxButtons.OK);
                        }
                        else
                        {
                            if (bytes_received.Count == 1 && bytes_received[0] == 0xFF)
                            {
                                SetDiagnosticTestInProgressStatus(false, ref diagnostic_test_in_progress);
                            }
                            else
                            {
                                MessageBox.Show("Failed to terminate diagnostic test in progress", "Error", MessageBoxButtons.OK);
                            }
                        }
                    }
                    else
                    {
                        MessageBox.Show("Failed to terminate diagnostic test in progress", "Error", MessageBoxButtons.OK);
                    }
                    */

                    if (!Transmit(cterminate_diagnostic_test, false))
                    {
                        Trace.WriteLine("Failed to terminate diagnostic test in progress");
                        MessageBox.Show("Failed to terminate diagnostic test in progress", "Error", MessageBoxButtons.OK);
                    }
                }

                foreach (CheckBox cb in DTcbs)
                {
                    if (cb != null)
                    {
                        cb.Enabled = false;
                    }
                }

                pcBatteryVoltage.TimerTickGetValueForChart -= UpdateBatteryVoltagePC;
                pcThrottleAngle.TimerTickGetValueForChart -= UpdateThrottleAnglePC;
                pcMAP.TimerTickGetValueForChart -= UpdateMAPPC;
                pcAirTemp.TimerTickGetValueForChart -= UpdateAirTempPC;
                pcWaterTemp.TimerTickGetValueForChart -= UpdateWaterTempPC;
                pcRPM.TimerTickGetValueForChart -= UpdateRPMPC;
                pcInjectorDuration.TimerTickGetValueForChart -= UpdateInjectorDurationPC;
                pcIgnitionAdvance.TimerTickGetValueForChart -= UpdateIgnitionAdvancePC;

                tsslIsoCode.Text = ciso_code;
                tsslECUInitialized.Text = cecu_initialized + "No";
                tsslRepairCode.Text = crepair_code;
#if DoGetAvailableMode1PIDs
                available_MODE1_PIDs = null;
#endif
                repair_code = null;
                tbAirTemp.Text = "";
                tbBatteryVoltage.Text = "";
                tbMAP.Text = "";
                tbThrottleAngle.Text = "";
                tbTrimmerPos.Text = "";
                tbWaterTemp.Text = "";
                tbRPM.Text = "";
                tbInjectorDuration.Text = "";
                tbIgnitionAdvance.Text = "";
                pcIgnitionAdvance.Clear();
                pcAirTemp.Clear();
                pcBatteryVoltage.Clear();
                pcInjectorDuration.Clear();
                pcMAP.Clear();
                pcRPM.Clear();
                pcThrottleAngle.Clear();
                pcWaterTemp.Clear();
                tbPollECU.Text = "";

                if (dialog != null)
                {
                    DisplayDialog("", false);
                }

                if (bwCommsInit != null)
                {
                    if (bwCommsInit.IsBusy)
                    {
                        bwCommsInit.CancelAsync();
                        serial_comms.Abort();
                        if (bwCommsInit != null)
                        {
                            try
                            {
                                while (bwCommsInit.IsBusy)
                                {
                                    Application.DoEvents();
                                }
                                bwCommsInit.Dispose();
                                bwCommsInit = null;
                            }
                            catch
                            {
                                // do nothing
                            }
                        }
                    }
                }

                if (poll_ecu_data != null)
                {
                    abort_poll_ecu_data = true;
                    poll_ecu_data.Abort();
                    while (poll_ecu_data.IsAlive)
                    {
                        Application.DoEvents();
                    }

                    poll_ecu_data = null;
                }

                if (update_values != null)
                {
                    update_values.Abort();
                    while (update_values.IsAlive)
                    {
                        Application.DoEvents();
                    }

                    update_values = null;
                }

                if (dt_termination_listener != null)
                {
                    dt_termination_listener.Abort();
                    while (dt_termination_listener.IsAlive)
                    {
                        Application.DoEvents();
                    }

                    dt_termination_listener = null;
                }

                serial_comms.CloseCommPort();

                SetPCsTimerMode(false);
            }
        }
        #endregion

        #region bwCommsInit_DoWork
        private void bwCommsInit_DoWork(object sender, DoWorkEventArgs e)
        {
            e.Result = serial_comms.Init((String)e.Argument);
            if (!(bool)e.Result)
            {
                DisplayMessage("Failed to initialize comms", true, true);
            }
        }
        #endregion

        #region bwCommsInit_RunWorkerCompleted
        private void bwCommsInit_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            bwCommsInit.Dispose();
            bwCommsInit = null;

            DisplayDialog("", false);
            poll_ecu_data = null;
            update_values = null;

            if ((bool)e.Result)
            {
                if (serial_comms.InitComms())
                {
                    if (serial_comms.ISOCode == null)
                    {
                        Trace.WriteLine("ISOCode == NULL");
                        tsslIsoCode.Text = ciso_code + "Error";
                    }
                    else
                    {
                        for (int i = 0; i < serial_comms.ISOCode.Length; i++)
                        {
                            tsslIsoCode.Text += serial_comms.ISOCode[i].ToString("X2");
                            if (i < serial_comms.ISOCode.Length - 1)
                            {
                                tsslIsoCode.Text += "-";
                            }
                        }
                    }

                    while (serial_comms.ECU_Initialized != DialogResult.Yes)
                    {
                        if (serial_comms.ECU_Initialized == DialogResult.No)
                        {
                            if (clbAvailable_Comm_Ports.SelectedIndex > -1)
                            {
                                DisplayMessage("Closing comm port", true, true);
                                clbAvailable_Comm_Ports.SetItemChecked(clbAvailable_Comm_Ports.SelectedIndex, false);
                            }

                            return;
                        }

                        Application.DoEvents();
                    }

                    tsslECUInitialized.Text = cecu_initialized + (serial_comms.ECU_Initialized == DialogResult.Yes ? "Yes" : "No");

#if DoGetAvailableMode1PIDs
                    if (!GetAvailableMode1PIDs())
                    {
                        DisplayMessage("Failed to retrieve available MODE 1 PIDs! Closing COM port...", true, true);
                        if (clbAvailable_Comm_Ports.SelectedIndex > -1)
                        {
                            clbAvailable_Comm_Ports.SetItemChecked(clbAvailable_Comm_Ports.SelectedIndex, false);
                        }
                        return;
                    }
#endif

                    Trace.WriteLine("Initializing ECU polling...");
                    poll_ecu_data = new Thread(new ThreadStart(PollECUData));
                    poll_ecu_data.IsBackground = true;
                    poll_ecu_data.Start();

                    if (check_for_ecu_errors_when_connected)
                    {
                        Trace.WriteLine("Checking for ECU errors at connected");
                        check_for_errors_timer.Interval = Convert.ToInt32(GetCfgFileEntry(ccfgcheckforecuerrorstime)[0]);
                        check_for_errors_timer.Tick += new EventHandler(CheckForErrorsTimerTick);
                        check_for_errors_timer.Start();
                    }

                    //SetPCsEnabledDisabled(true);
                }
                else
                {
                    Trace.WriteLine("SerialComms was not initialized");

                    if (clbAvailable_Comm_Ports.SelectedIndex > -1)
                    {
                        DisplayMessage("Closing comm port", true, true);
                        clbAvailable_Comm_Ports.SetItemChecked(clbAvailable_Comm_Ports.SelectedIndex, false);
                    }

                    return;
                }
            }
            else
            {
                Trace.WriteLine("Background worker thread completed in error");

                if (clbAvailable_Comm_Ports.SelectedIndex > -1)
                {
                    DisplayMessage("Closing comm port", true, true);
                    clbAvailable_Comm_Ports.SetItemChecked(clbAvailable_Comm_Ports.SelectedIndex, false);
                }

                return;
            }

            if (poll_ecu_data == null)
            {
                DisplayMessage("poll ECU data == NULL", true, true);

                if (clbAvailable_Comm_Ports.SelectedIndex > -1)
                {
                    DisplayMessage("Closing comm port", true, true);
                    clbAvailable_Comm_Ports.SetItemChecked(clbAvailable_Comm_Ports.SelectedIndex, false);
                }
                
                return;
            }

            while (poll_ecu_data != null && (poll_ecu_data.IsAlive || ((poll_ecu_data.ThreadState & System.Threading.ThreadState.Stopped) != 0)) && !form_closing)
            {
                Application.DoEvents();

                if (abort_poll_ecu_data)
                {
                    break;
                }
            }

            if (clbAvailable_Comm_Ports.SelectedIndex > -1)
            {
                DisplayMessage("Closing comm port", true, true);
                clbAvailable_Comm_Ports.SetItemChecked(clbAvailable_Comm_Ports.SelectedIndex, false);
            }

            Trace.WriteLine("Poll ECU data ended");

            if (poll_ecu_data == null)
            {
                return;
            }

            if (form_closing)
            {
                Trace.WriteLine("Form closing");
                return;
            }

            DisplayMessage("Communications lost with ECU! Closing COM port", true, true);
            MessageBox.Show("Communications lost with ECU! Closing COM port", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            if (clbAvailable_Comm_Ports.SelectedIndex > -1)
            {
                clbAvailable_Comm_Ports.SetItemChecked(clbAvailable_Comm_Ports.SelectedIndex, false);
            }
        }
        #endregion

        #region DiscardInBuffer
        private void DiscardInBuffer()
        {
            if (serial_comms == null || serial_comms.ECU_Initialized != DialogResult.Yes)
            {
                return;
            }

            serial_comms.DiscardInBuffer();
        }
        #endregion

        #region DiscardOutBuffer
        private void DiscardOutBuffer()
        {
            if (serial_comms == null || serial_comms.ECU_Initialized != DialogResult.Yes)
            {
                return;
            }

            serial_comms.DiscardOutBuffer();
        }
        #endregion

        #region Transmit
        private bool Transmit(Byte byte_to_transmit, bool add_transmit_delay)
        {
            if (serial_comms == null || serial_comms.ECU_Initialized != DialogResult.Yes)
            {
                return false;
            }

            try
            {
                serial_comms.Transmit(byte_to_transmit, add_transmit_delay);
            }
            catch (ThreadAbortException)
            {
                // do nothing
            }
            catch (Exception t)
            {
                DisplayMessage("Transmit exception: " + t.Message, true, true);
                return false;
            }

            return true;
        }

        private bool Transmit(Byte[] bytes_to_transmit, int time_between_bytes_in_ms)
        {
            if (serial_comms == null || serial_comms.ECU_Initialized != DialogResult.Yes)
            {
                return false;
            }

            try
            {
                serial_comms.Transmit(bytes_to_transmit, time_between_bytes_in_ms);
            }
            catch (ThreadAbortException)
            {
                // do nothing
            }
            catch (Exception t)
            {
                DisplayMessage("Transmit exception: " + t.Message, true, true);
                return false;
            }

            return true;
        }
        #endregion

        #region TransmitByte
        internal bool TransmitByte(byte b, bool expecting_return_byte)
        {
            byte_to_receive_expected = expecting_return_byte;
            byte_to_send = b;
            send_byte = true;
            while (send_byte)
            {
                Application.DoEvents();
            }

            return byte_to_send_result;
        }
        #endregion

        #region Receive
        private List<Byte> Receive(int number_of_bytes_to_receive, int timeout)
        {
            try
            {
                // spec says time between request and answer is 4ms < T < 300ms
                bytes_received = serial_comms.Receive(number_of_bytes_to_receive, timeout);

                // spec says have to wait at least 4ms before next demand
                Thread.Sleep(cnext_PID_sleep);

                return bytes_received;
            }
            catch (ThreadAbortException)
            {
                // do nothing
            }
            catch (Exception r)
            {
                DisplayMessage("Receive exception: " + r.Message, true, true);
                return null;
            }

            return null;
        }
        #endregion

        #region ConvertByteToBitArray
        internal BitArray ConvertByteToBitArray(byte b)
        {
            BitArray ba = new BitArray(8, false);
            for (int i = 0; i < 8; i++)
            {
                ba[i] = ((b & (byte)Math.Pow(2, i)) == 0 ? false : true);
            }

            return ba;
        }
        #endregion

#if DoGetAvailableMode1PIDs
        #region GetAvailableMode1PIDs
        private bool GetAvailableMode1PIDs()
        {
            DisplayDialog("Determining available MODE $1 PIDs...", true);
            DisplayMessage("Determining available MODE $1 PIDs...", true, true);

            if (!Transmit(new byte[] { 0x00 }))
            {
                DisplayDialog("", false);
                return false;
            }

            available_MODE1_PIDs = Receive(-1, cPID_receive_timeout * 20);
            if (available_MODE1_PIDs == null)
            {
                DisplayDialog("", false);
                return false;
            }

            DisplayDialog("", false);

            if (available_MODE1_PIDs[0] == 0x0)
            {
                DisplayMessage("ECU reported available MODE $1 PIDs as 0x0", true, true);
                MessageBox.Show("ECU reported available MODE $1 PIDs as 0x0", "ECU query", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
            {
                String mes = "Available MODE $1 PIDs:\r\n\r\n";

                foreach (Byte b in available_MODE1_PIDs)
                {
                    mes += "$" + b.ToString("X2") + " ";
                }

                DisplayMessage(mes, true, true);
                MessageBox.Show(mes, "ECU query", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }

            return true;
        }
        #endregion
#endif

        #region PollECUData
        private void PollECUData()
        {
            Trace.WriteLine("Poll ECU data start");

            bytes_received = null;
            byte_to_transmit = 0x0;
            bool tdw_um999x_found = false;

            // ensure buffers are clear
            Trace.WriteLine("Poll ECU data: clearing in and out buffers");
            DiscardInBuffer();
            DiscardOutBuffer();

            #region Repair code
            // Repair Code
            Trace.WriteLine("\r\nRetrieving repair code...");

            repair_code = new byte[11];
            String strrepair_code = "";

            // repair code or part # is 11 bytes in length
            for (int rc = 0; rc < repair_code.Length; rc++)
            {
                byte_to_transmit = (byte)(rc + 0x17);

                if (!Transmit(byte_to_transmit, true))
                {
                    DisplayMessage("Failed to transmit byte for repair code", true, true);
                    abort_poll_ecu_data = true;
                    return;
                }

                bytes_received = Receive(2, cPID_receive_timeout);
                if (bytes_received == null || bytes_received[0] != byte_to_transmit)
                {
                    if (bytes_received == null)
                    {
                        DisplayMessage("Failed to receive byte for repair code", true, true);
                        abort_poll_ecu_data = true;
                        return;
                    }
                    else
                    {
                        DisplayMessage("Failed to receive byte for repair code. Byte received=0x" + bytes_received[0].ToString("X2") + "\r\nTrying single byte...", true, true);

                        Trace.WriteLine("Poll ECU data: clearing in and out buffers");
                        DiscardInBuffer();
                        DiscardOutBuffer();

                        byte_to_transmit = (byte)(rc + 0x17);

                        if (!Transmit(byte_to_transmit, true))
                        {
                            DisplayMessage("Failed to transmit byte for repair code", true, true);
                            abort_poll_ecu_data = true;
                            return;
                        }

                        bytes_received = Receive(1, cPID_receive_timeout);
                        if (bytes_received == null || bytes_received[0] != byte_to_transmit)
                        {
                            DisplayMessage("Failed to receive byte for repair code - single byte", true, true);
                            abort_poll_ecu_data = true;
                            return;
                        }

                        bytes_received = Receive(1, cPID_receive_timeout);
                        if (bytes_received == null)
                        {
                            DisplayMessage("Failed to receive repair code byte", true, true);
                            abort_poll_ecu_data = true;
                            return;
                        }

                        repair_code[rc] = bytes_received[0];
                        strrepair_code += Convert.ToChar(repair_code[rc]);
                    }
                }
                else
                {
                    repair_code[rc] = bytes_received[1];
                    strrepair_code += Convert.ToChar(repair_code[rc]);
                }
            }

            tsslRepairCode.Text = crepair_code + strrepair_code;
            Trace.WriteLine(tsslRepairCode.Text);

            if (strrepair_code == "TDW_UM999X ")
            {
                tdw_um999x_found = true;
                Trace.WriteLine("Repair code of TDW_UM999X found!");
            }
            #endregion

            // read out all the flags at the start
            #region ECU flags
            ecuflags = new BitArray(8, false);

            if (tdw_um999x_found)
            {
                Trace.WriteLine("Retrieving ECU flags");
                if (!Transmit(cecuflags, true))
                {
                    DisplayMessage("Failed to transmit byte for ECU flags", true, true);
                    abort_poll_ecu_data = true;
                    return;
                }

                bytes_received = Receive(1, cPID_receive_timeout);
                if (bytes_received == null || bytes_received[0] != cecuflags)
                {
                    DisplayMessage("Failed to receive byte for ECU flags", true, true);
                    abort_poll_ecu_data = true;
                    return;
                }

                bytes_received = Receive(1, cPID_receive_timeout);
                if (bytes_received == null)
                {
                    // do nothing as chip doesn't support this
                    DisplayMessage("Failed to receive ECU flags", true, true);
                }
                else
                {
                    Trace.WriteLine("ECU flags:");
                    for (int i = 0; i < 8; i++)
                    {
                        ecuflags[i] = ((bytes_received[0] & (byte)Math.Pow(2, i)) == 0 ? true : false);
                        Trace.WriteLine(ecuflags[i].ToString());
                    }

                    Trace.WriteLine("");

                    SetHighSpeedComms();
                }
            }
            #endregion

            #region TDW flags
            if (ecuflags[cecuflags_supports_tdwflags])
            {
                Trace.WriteLine("Retrieving TDW flags");

                GetTDWFlags();
            }
            else
            {
                Trace.WriteLine("ECU does not support TDW flags");
            }
            #endregion

            #region TDW Disable trims
            if (ecuflags[cecuflags_supports_disable_trims])
            {
                Trace.WriteLine("Retrieving disable trims");

                GetTDWDisableTrims();
            }
            else
            {
                Trace.WriteLine("ECU does not support disable trims");
            }
            #endregion

            #region TDW global trims
            if (ecuflags[cecuflags_supports_global_trims])
            {
                Trace.WriteLine("Retrieving global trims");

                GetTDWGlobalTrims();
            }
            else
            {
                Trace.WriteLine("ECU does not support global trims");
            }
            #endregion

            #region Init variables
            battery_voltage = 0.0f;
            pc_battery_voltage = 0.0f;
            throttle_angle = 0.0f;
            pc_throttle_angle = 0.0f;
            manifold_absolute_pressure = 0.0f;
            pc_manifold_absolute_pressure = 0.0f;
            air_temp = 0.0f;
            pc_air_temp = 0.0f;
            water_temp = 0.0f;
            pc_water_temp = 0.0f;
            trimmer_position = 0.0f;
            rpm = 0.0f;
            pc_rpm = 0.0f;
            rpm_bytes = null;
            irpm = 0;
            injector_duration = 0.0f;
            pc_injector_duration = 0.0f;
            injector_duration_bytes = null;
            iinjector_duration = 0;
            ignition_advance = 0.0f;
            pc_ignition_advance = 0.0f;
            fuel_trim = 0;
            fuel_trim_power_on = 0;
            #endregion

            bool eventsset = false;

            hpt_poll_ecu_data.Timer(HighPrecisionTimer.HPT.Reset);

            Trace.WriteLine("Enabling diagnostic test buttons");
            foreach (CheckBox cb in DTcbs)
            {
                if (cb != null)
                {
                    cb.Enabled = true;
                }
            }

            Trace.WriteLine("Initializing updating of values...");
            update_values = new Thread(new ThreadStart(UpdateValues));
            update_values.IsBackground = true;
            update_values.Start();

            Trace.WriteLine("Poll ECU data loop active");

            try
            {
                while (!form_closing)
                {
                    try
                    {
                        if (update_values == null || !update_values.IsAlive)
                        {
                            Trace.WriteLine("Update values == NULL || !Alive");
                            break;
                        }

                        #region Dump EEPROM
                        if (dump_EEPROM)
                        {
                            if (EEPROM_contents == null)
                            {
                                Trace.WriteLine("Dump EEPROM active");

                                tbPollECU.Text = "DISABLED";
                                EEPROM_contents = new Byte[cEEPROMsize];
                                dump_EEPROM_byte_index = 0;
                                DiscardInBuffer();
                                DiscardOutBuffer();

                                if (!Transmit(cDumpEEPROM, true))
                                {
                                    dump_EEPROM = false;
                                    EEPROM_contents = null;
                                    continue;
                                }

                                bytes_received = Receive(1, cPID_receive_timeout);
                                if (bytes_received == null || bytes_received[0] != cDumpEEPROM)
                                {
                                    dump_EEPROM = false;
                                    EEPROM_contents = null;
                                    Transmit(ctdwdisableall, false);
                                    bytes_received = Receive(1, cPID_receive_timeout);
                                }
                                Trace.WriteLine("Byte received = " + bytes_received[0].ToString("X"));

                                continue;
                            }
                            else
                            {
                                if (!Transmit(cDumpEEPROM, false))
                                {
                                    dump_EEPROM = false;
                                    EEPROM_contents = null;
                                    Transmit(ctdwdisableall, false);
                                    bytes_received = Receive(1, cPID_receive_timeout);
                                    continue;
                                }

                                bytes_received = Receive(2, cPID_receive_timeout);
                                if (bytes_received == null || bytes_received.Count == 1 || bytes_received[0] != cDumpEEPROM)
                                {
                                    dump_EEPROM = false;
                                    EEPROM_contents = null;
                                    Transmit(ctdwdisableall, false);
                                    bytes_received = Receive(1, cPID_receive_timeout);
                                    continue;
                                }
                                Trace.WriteLine("Byte received = " + bytes_received[1].ToString("X"));

                                EEPROM_contents[dump_EEPROM_byte_index++] = bytes_received[1];
                                if (dump_EEPROM_byte_index == cEEPROMsize)
                                {
                                    dump_EEPROM = false;
                                }

                                continue;
                            }
                        }
                        #endregion
                        else
                        {
                            if (!dump_Byte)
                            {
                                //Application.DoEvents();

                                #region FGSTAT
                                if (master_poll_enable)
                                {
                                    if (!Transmit(cfgstat, false))
                                    {
                                        break;
                                    }

                                    bytes_received = Receive(2, cPID_receive_timeout);
                                    if (bytes_received == null)
                                    {
                                        break;
                                    }
                                    else
                                    {
                                        if (bytes_received.Count == 2 && bytes_received[0] == cfgstat)
                                        {
                                            fgstat = bytes_received[1];
                                        }
                                    }
                                }
                                #endregion

                                #region Battery voltage
                                // Battery voltage
                                if (cbBatteryVoltage.Checked && master_poll_enable)
                                {
                                    if (!Transmit(0xA, false))
                                    {
                                        break;
                                    }

                                    bytes_received = Receive(2, cPID_receive_timeout);
                                    if (bytes_received == null)
                                    {
                                        break;
                                    }
                                    else
                                    {
                                        if (bytes_received.Count == 2 && bytes_received[0] == 0xA)
                                        {
                                            battery_voltage = bytes_received[1] * 0.0625f;
                                            //tbBatteryVoltage.Text = battery_voltage.ToString("F2") + "Vdc";
                                            //DisplayMessage("Battery voltage: " + battery_voltage.ToString("F2") + "Vdc", false, true);
                                        }
                                    }
                                }
                                #endregion

                                #region Throttle angle
                                // Throttle angle
                                if (cbThrottleAngle.Checked && master_poll_enable)
                                {
                                    if (!Transmit(0x09, false))
                                    {
                                        break;
                                    }

                                    bytes_received = Receive(2, cPID_receive_timeout);
                                    if (bytes_received == null)
                                    {
                                        break;
                                    }
                                    else
                                    {
                                        if (bytes_received.Count == 2 && bytes_received[0] == 0x9)
                                        {
                                            throttle_angle = (bytes_received[1] * 0.4234f) - 2.9638f;
                                            //tbThrottleAngle.Text = throttle_angle.ToString("F2") + Convert.ToChar(0xB0);
                                            //DisplayMessage("Throttle angle: " + throttle_angle.ToString("F2") + Convert.ToChar(0xB0), false, true);
                                        }
                                    }
                                }
                                #endregion

                                #region MAP
                                // Manifold absolute pressure
                                if (cbMAP.Checked && master_poll_enable)
                                {
                                    if (!Transmit(0x06, false))
                                    {
                                        break;
                                    }

                                    bytes_received = Receive(2, cPID_receive_timeout);
                                    if (bytes_received == null)
                                    {
                                        break;
                                    }
                                    else
                                    {
                                        if (bytes_received.Count == 2 && bytes_received[0] == 0x6)
                                        {
                                            manifold_absolute_pressure = bytes_received[1] * 3;
                                            //tbMAP.Text = manifold_absolute_pressure.ToString("F2") + "mmHg";
                                            //DisplayMessage("Manifold absolute pressure: " + manifold_absolute_pressure.ToString("F2") + "mmHg", false, true);
                                        }
                                    }
                                }
                                #endregion

                                #region Air temp
                                // Air temperature
                                if (cbAirTemp.Checked && master_poll_enable)
                                {
                                    if (!Transmit(0x07, false))
                                    {
                                        break;
                                    }

                                    bytes_received = Receive(2, cPID_receive_timeout);
                                    if (bytes_received == null)
                                    {
                                        break;
                                    }
                                    else
                                    {
                                        if (bytes_received.Count == 2 && bytes_received[0] == 0x7)
                                        {
                                            air_temp = bytes_received[1] - 40;
                                            //tbAirTemp.Text = air_temp.ToString("F2") + Convert.ToChar(0xB0) + "C" + "\t" + CelsiusToFahrenheit(air_temp).ToString("F2") + Convert.ToChar(0xB0) + "F";
                                            //DisplayMessage("Air temp: " + air_temp.ToString("F2") + Convert.ToChar(0xB0) + "C" + "\t" + CelsiusToFahrenheit(air_temp).ToString("F2") + Convert.ToChar(0xB0) + "F", false, true);
                                        }
                                    }
                                }
                                #endregion

                                #region Water temp
                                // Water temperature
                                if (cbWaterTemp.Checked && master_poll_enable)
                                {
                                    if (!Transmit(0x08, false))
                                    {
                                        break;
                                    }

                                    bytes_received = Receive(2, cPID_receive_timeout);
                                    if (bytes_received == null)
                                    {
                                        break;
                                    }
                                    else
                                    {
                                        if (bytes_received.Count == 2 && bytes_received[0] == 0x8)
                                        {
                                            water_temp = bytes_received[1] - 40;
                                            //tbWaterTemp.Text = water_temp.ToString("F2") + Convert.ToChar(0xB0) + "C" + "\t" + CelsiusToFahrenheit(water_temp).ToString("F2") + Convert.ToChar(0xB0) + "F";
                                            //DisplayMessage("Water temp: " + water_temp.ToString("F2") + Convert.ToChar(0xB0) + "C" + "\t" + CelsiusToFahrenheit(water_temp).ToString("F2") + Convert.ToChar(0xB0) + "F", false, true);
                                        }
                                    }
                                }
                                #endregion

                                #region Trimmer position
                                // Trimmer position
                                if (cbTPSTrim.Checked && master_poll_enable)
                                {
                                    if (!Transmit(0x0F, false))
                                    {
                                        break;
                                    }

                                    bytes_received = Receive(2, cPID_receive_timeout);
                                    if (bytes_received == null)
                                    {
                                        break;
                                    }
                                    else
                                    {
                                        if (bytes_received.Count == 2 && bytes_received[0] == 0xF)
                                        {
                                            trimmer_position = bytes_received[1] - 127;
                                            //tbTrimmerPos.Text = trimmer_position.ToString("F0");
                                            //DisplayMessage("Trimmer position: " + trimmer_position.ToString("F0"), false, true);
                                        }
                                    }
                                }
                                #endregion

                                #region RPM
                                // RPM
                                if (cbRPM.Checked && master_poll_enable)
                                {
                                    if (!Transmit(0x01, false))
                                    {
                                        break;
                                    }

                                    bytes_received = Receive(2, cPID_receive_timeout);
                                    if (bytes_received == null)
                                    {
                                        break;
                                    }
                                    else
                                    {
                                        rpm_bytes = new byte[2] { 0x0, 0x0 };

                                        if (bytes_received.Count == 2 && bytes_received[0] == 0x1)
                                        {
                                            rpm_bytes[1] = bytes_received[1];

                                            if (!Transmit(0x02, false))
                                            {
                                                break;
                                            }

                                            bytes_received = Receive(2, cPID_receive_timeout);
                                            if (bytes_received == null)
                                            {
                                                break;
                                            }
                                            else
                                            {
                                                if (bytes_received.Count == 2 && bytes_received[0] == 0x2)
                                                {
                                                    rpm_bytes[0] = bytes_received[1];
                                                    irpm = BitConverter.ToInt16(rpm_bytes, 0);
                                                    if (irpm < 0)
                                                    {
                                                        rpm = 0.0f;
                                                    }
                                                    else
                                                    {
                                                        rpm = 15000000.0f / irpm;
                                                    }
                                                    //tbRPM.Text = rpm.ToString("F2");
                                                    //DisplayMessage("RPM: " + rpm.ToString("F2"), false, true);
                                                }
                                            }
                                        }
                                    }
                                }
                                #endregion

                                #region Injector duration
                                // Injector Duration
                                if (cbInjectorDuration.Checked && master_poll_enable)
                                {
                                    if (!Transmit(0x03, false))
                                    {
                                        break;
                                    }

                                    bytes_received = Receive(2, cPID_receive_timeout);
                                    if (bytes_received == null)
                                    {
                                        break;
                                    }
                                    else
                                    {
                                        injector_duration_bytes = new byte[2] { 0x0, 0x0 };

                                        if (bytes_received.Count == 2 && bytes_received[0] == 0x3)
                                        {
                                            injector_duration_bytes[1] = bytes_received[1];

                                            if (!Transmit(0x04, false))
                                            {
                                                break;
                                            }

                                            bytes_received = Receive(2, cPID_receive_timeout);
                                            if (bytes_received == null)
                                            {
                                                break;
                                            }
                                            else
                                            {
                                                if (bytes_received.Count == 2 && bytes_received[0] == 0x4)
                                                {
                                                    injector_duration_bytes[0] = bytes_received[1];
                                                    iinjector_duration = BitConverter.ToInt16(injector_duration_bytes, 0);
                                                    if (iinjector_duration < 0)
                                                    {
                                                        injector_duration = 0.0f;
                                                    }
                                                    else
                                                    {
                                                        injector_duration = iinjector_duration / 500.0f;
                                                    }
                                                    //tbInjectorDuration.Text = injector_duration.ToString("F2") + "ms";
                                                    //DisplayMessage("Injector Duration: " + injector_duration.ToString("F2") + "ms", false, true);
                                                }
                                            }
                                        }
                                    }
                                }
                                #endregion

                                #region Ignition advance
                                // Ignition Advance
                                if (cbIgnitionAdvance.Checked && master_poll_enable)
                                {
                                    if (!Transmit(0x05, false))
                                    {
                                        break;
                                    }

                                    bytes_received = Receive(2, cPID_receive_timeout);
                                    if (bytes_received == null)
                                    {
                                        break;
                                    }
                                    else
                                    {
                                        if (bytes_received.Count == 2 && bytes_received[0] == 0x5)
                                        {
                                            ignition_advance = bytes_received[1] / 2.0f;
                                            //tbIgnitionAdvance.Text = ignition_advance.ToString("F2") + Convert.ToChar(0xB0);
                                            //DisplayMessage("Ignition Advance: " + ignition_advance.ToString("F2") + Convert.ToChar(0xB0), false, true);
                                        }
                                    }
                                }
                                #endregion

                                #region Fuel trim raw
                                // Fuel trim
                                if (cbFuelTrimRaw.Checked && master_poll_enable)
                                {
                                    if (!Transmit(0x22, false))
                                    {
                                        break;
                                    }

                                    bytes_received = Receive(2, cPID_receive_timeout);
                                    if (bytes_received == null)
                                    {
                                        break;
                                    }
                                    else
                                    {
                                        if (bytes_received.Count == 2 && bytes_received[0] == 0x22)
                                        {
                                            fuel_trim = Convert.ToInt32(bytes_received[1]);
                                            //tbFuelTrim.Text = fuel_trim.ToString("X");
                                            //DisplayMessage("Fuel trim: " + fuel_trim.ToString("X"));
                                        }
                                    }
                                }
                                #endregion

                                #region Fuel trim power on
                                // Fuel trim power on
                                if (cbFuelTrimPowerOn.Checked && master_poll_enable)
                                {
                                    if (!Transmit(0x23, false))
                                    {
                                        break;
                                    }

                                    bytes_received = Receive(2, cPID_receive_timeout);
                                    if (bytes_received == null)
                                    {
                                        break;
                                    }
                                    else
                                    {
                                        if (bytes_received.Count == 2 && bytes_received[0] == 0x23)
                                        {
                                            fuel_trim_power_on = Convert.ToInt32(bytes_received[1]);
                                            //tbFuelTrimPowerOn.Text = fuel_trim_power_on.ToString("X");
                                            //DisplayMessage("Fuel trim power on: " + fuel_trim_power_on.ToString("X"));
                                        }
                                    }
                                }
                                #endregion

                                #region Start enrichment
                                // Start enrichment
                                if (cbStartEnrichment.Checked && master_poll_enable)
                                {
                                    if (!Transmit(0x24, false))
                                    {
                                        break;
                                    }

                                    bytes_received = Receive(2, cPID_receive_timeout);
                                    if (bytes_received == null)
                                    {
                                        break;
                                    }
                                    else
                                    {
                                        if (bytes_received.Count == 2 && bytes_received[0] == 0x24)
                                        {
                                            start_enrichment = Convert.ToInt32(bytes_received[1]);
                                            //tbStartEnrichment.Text = start_enrichment.ToString("X");
                                            //DisplayMessage("Start enrichment: " + start_enrichment.ToString("X"));
                                        }
                                    }
                                }
                                #endregion

                                #region Spare 1
                                // Spare 1
                                if (cbSpare1.Checked && master_poll_enable)
                                {
                                    if (!Transmit(0x25, false))
                                    {
                                        break;
                                    }

                                    bytes_received = Receive(2, cPID_receive_timeout);
                                    if (bytes_received == null)
                                    {
                                        break;
                                    }
                                    else
                                    {
                                        if (bytes_received.Count == 2 && bytes_received[0] == 0x25)
                                        {
                                            spare1 = Convert.ToInt32(bytes_received[1]);
                                            //tbSpare1.Text = spare1.ToString("X");
                                            //DisplayMessage("Spare 1: " + spare1.ToString("X"));
                                        }
                                    }
                                }
                                #endregion

                                #region Spare 2
                                // Spare 2
                                if (cbSpare2.Checked && master_poll_enable)
                                {
                                    if (!Transmit(0x28, false))
                                    {
                                        break;
                                    }

                                    bytes_received = Receive(2, cPID_receive_timeout);
                                    if (bytes_received == null)
                                    {
                                        break;
                                    }
                                    else
                                    {
                                        if (bytes_received.Count == 2 && bytes_received[0] == 0x28)
                                        {
                                            spare2 = Convert.ToInt32(bytes_received[1]);
                                            //tbSpare2.Text = spare2.ToString("X");
                                            //DisplayMessage("Spare 2: " + spare2.ToString("X"));
                                        }
                                    }
                                }
                                #endregion

                                /*
                    #region Spare 2 - CurrentSparkAdvance_Word
                    if (cbSpare2.Checked && master_poll_enable)
                    {
                        if (!Transmit(0x29, false))
                        {
                            return;
                        }

                        bytes_received = Receive(2, cPID_receive_timeout);
                        if (bytes_received == null)
                        {
                            break;
                        }
                        else
                        {
                            current_spark_advance_word_bytes = new byte[2] { 0x0, 0x0 };

                            if (bytes_received.Count == 2 && bytes_received[0] == 0x29)
                            {
                                current_spark_advance_word_bytes[1] = bytes_received[1];

                                if (!Transmit(0x2A, false))
                                {
                                    return;
                                }

                                bytes_received = Receive(2, cPID_receive_timeout);
                                if (bytes_received == null)
                                {
                                    break;
                                }
                                else
                                {
                                    if (bytes_received.Count == 2 && bytes_received[0] == 0x2A)
                                    {
                                        current_spark_advance_word_bytes[0] = bytes_received[1];
                                        icurrent_spark_advance_word = BitConverter.ToInt16(current_spark_advance_word_bytes, 0);
                                    }
                                }
                            }
                        }
                    }
                    #endregion
                    */

                                #region eventsset
                                if (!eventsset)
                                {
                                    Trace.WriteLine("Events set");
                                    eventsset = true;
                                    pcBatteryVoltage.TimerTickGetValueForChart += UpdateBatteryVoltagePC;
                                    pcThrottleAngle.TimerTickGetValueForChart += UpdateThrottleAnglePC;
                                    pcMAP.TimerTickGetValueForChart += UpdateMAPPC;
                                    pcAirTemp.TimerTickGetValueForChart += UpdateAirTempPC;
                                    pcWaterTemp.TimerTickGetValueForChart += UpdateWaterTempPC;
                                    pcRPM.TimerTickGetValueForChart += UpdateRPMPC;
                                    pcInjectorDuration.TimerTickGetValueForChart += UpdateInjectorDurationPC;
                                    pcIgnitionAdvance.TimerTickGetValueForChart += UpdateIgnitionAdvancePC;

                                    SetPCsTimerMode(false);
                                }
                                #endregion

                                #region Check for errors
                                if (check_for_errors)
                                {
                                    Trace.WriteLine("Check for errors active");

                                    // reset flag
                                    check_for_errors = false;

                                    // display message so user knows this is going on
                                    DisplayMessage("Checking ECU for errors...", true, true);

                                    String message = GetAllErrors(true);
                                    if (message == null)
                                    {
                                        MessageBox.Show("Error retrieving ECU errors", "Error", MessageBoxButtons.OK);
                                    }
                                    else
                                    {
                                        // get all the errors from the ECU
                                        // will probably have this set another flag so that this thread isn't paused by the messagebox function
                                        MessageBox.Show(message, "ECU errors", MessageBoxButtons.OK);
                                    }
                                }
                                #endregion

                                #region Get TDW disable trims
                                if (get_tdwdisabletrims)
                                {
                                    Trace.WriteLine("Get TDW disable trims active");

                                    get_tdwdisabletrims = false;

                                    if (ecuflags[cecuflags_supports_disable_trims])
                                    {
                                        GetTDWDisableTrims();
                                    }
                                }
                                #endregion

                                #region Get TDW global trims
                                if (get_tdwglobaltrims)
                                {
                                    Trace.WriteLine("Get TDW global trims active");

                                    get_tdwglobaltrims = false;

                                    if (ecuflags[cecuflags_supports_global_trims])
                                    {
                                        GetTDWGlobalTrims();
                                    }
                                }
                                #endregion

                                #region Get TDW flags
                                if (get_tdwflags)
                                {
                                    Trace.WriteLine("Get TDW flags active");

                                    get_tdwflags = false;

                                    if (ecuflags[cecuflags_supports_tdwflags])
                                    {
                                        GetTDWFlags();
                                    }
                                }
                                #endregion
                            }

                            #region Byte to send
                            if (send_byte)
                            {
                                Trace.WriteLine("Byte to send active");

                                byte_to_receive_result = false;
                                byte_to_receive = 0;

                                byte_to_send_result = Transmit(byte_to_send, true);
                                if (!byte_to_send_result)
                                {
                                    send_byte = false;
                                }
                                else
                                {
                                    int number_of_bytes_to_receive = 1;
                                    if (byte_to_receive_expected)
                                    {
                                        byte_to_receive_expected = false;
                                        number_of_bytes_to_receive++;
                                    }
                                    bytes_received = Receive(number_of_bytes_to_receive, cPID_receive_timeout);
                                    if (bytes_received == null || bytes_received[0] != byte_to_send)
                                    {
                                        send_byte = false;
                                    }
                                    else
                                    {
                                        if (number_of_bytes_to_receive == 2)
                                        {
                                            byte_to_receive = bytes_received[1];
                                        }
                                        else
                                        {
                                            byte_to_receive = 0;
                                        }

                                        byte_to_receive_result = true;
                                        send_byte = false;
                                    }
                                }
                            }
                            #endregion
                        }

                        if (master_poll_enable && !dump_EEPROM && !send_byte)
                        {
                            //DisplayMessage("", false, true);
                            tbPollECU.Text = (hpt_poll_ecu_data.Timer(HighPrecisionTimer.HPT.GetElapsedTime) * 1000).ToString("F2") + "ms";
                        }
                        else
                        {
                            tbPollECU.Text = "DISABLED";
                        }
                    }
                    catch (Exception e)
                    {
                        Trace.WriteLine("Poll ECU data exception: " + e.Message);
                    }
                }
            }
            finally
            {
                Trace.WriteLine("Poll ECU data stop");
                abort_poll_ecu_data = true;
            }
        }
        #endregion

        #region SetLowSpeedComms
        private void SetLowSpeedComms()
        {
            if (!ecuflags[cecuflags_supports_high_speed_comms])
            {
                DisplayMessage("This ECU does not support low speed comms", true, true);
                return;
            }

            Trace.WriteLine("Setting low speed comms...");
            if (!Transmit(ccommslowspeed, true))
            {
                DisplayMessage("Failed to transmit byte for low speed comms", true, true);

                if (clbAvailable_Comm_Ports.SelectedIndex > -1)
                {
                    DisplayMessage("Closing comm port", true, true);
                    clbAvailable_Comm_Ports.SetItemChecked(clbAvailable_Comm_Ports.SelectedIndex, false);
                }

                return;
            }

            bytes_received = Receive(1, cPID_receive_timeout);
            if (bytes_received == null || bytes_received[0] != ccommslowspeed)
            {
                DisplayMessage("Failed to set low speed comms", true, true);
                return;
            }
            else
            {
                if (!serial_comms.SetLowSpeedComms())
                {
                    DisplayMessage("Failed to set low speed comms", true, true);
                    return;
                }

                DisplayMessage("Low speed comms set", true, true);
            }
        }
        #endregion

        #region SetHighSpeedComms
        private void SetHighSpeedComms()
        {
            if (!ecuflags[cecuflags_supports_high_speed_comms])
            {
                DisplayMessage("This ECU does not support high speed comms", true, true);
                return;
            }

            Trace.WriteLine("Setting high speed comms...");
            if (!Transmit(ccommshighspeed, true))
            {
                DisplayMessage("Failed to transmit byte for high speed comms", true, true);

                if (clbAvailable_Comm_Ports.SelectedIndex > -1)
                {
                    DisplayMessage("Closing comm port", true, true);
                    clbAvailable_Comm_Ports.SetItemChecked(clbAvailable_Comm_Ports.SelectedIndex, false);
                }

                return;
            }

            bytes_received = Receive(1, cPID_receive_timeout);
            if (bytes_received == null || bytes_received[0] != ccommshighspeed)
            {
                DisplayMessage("Failed to set high speed comms", true, true);
                return;
            }
            else
            {
                if (!serial_comms.SetHighSpeedComms())
                {
                    DisplayMessage("Failed to set high speed comms", true, true);
                    return;
                }

                DisplayMessage("High speed comms set", true, true);
            }
        }
        #endregion

        #region DumpECUByte
        internal bool DumpECUByte(int address)
        {
            if (!ecuflags[cecuflags_supports_byte_dump])
            {
                return false;
            }

            dump_Byte = true;

            try
            {
                // not expecting any return value yet...
                byte_to_receive_expected = false;

                // send the dump byte command
                byte_to_send = cDumpByte;
                send_byte = true;

                while (send_byte)
                {
                    Application.DoEvents();
                }

                if (!byte_to_send_result || !byte_to_receive_result)
                {
                    return false;
                }

                // send the MSB of the address
                byte_to_send = (byte)((address & 0x0000FF00) >> 8);
                send_byte = true;

                while (send_byte)
                {
                    Application.DoEvents();
                }

                if (!byte_to_send_result || !byte_to_receive_result)
                {
                    return false;
                }

                // expecting ECU to send value back to us
                byte_to_receive_expected = true;

                // send the LSB of the address
                byte_to_send = (byte)(address & 0x000000FF);
                send_byte = true;

                while (send_byte)
                {
                    Application.DoEvents();
                }

                if (!byte_to_send_result || !byte_to_receive_result)
                {
                    return false;
                }
            }
            catch (Exception e)
            {
                MessageBox.Show("Exception while dumping byte. Exception is " + e.Message);
                return false;
            }
            finally
            {
                dump_Byte = false;
            }

            return true;
        }
        #endregion

        #region GetTDWDisableTrims
        private void GetTDWDisableTrims()
        {
            tdwdisabletrims = new BitArray(8, false);

            if (!ecuflags[cecuflags_supports_disable_trims])
            {
                DisplayMessage("ECU does not support disable trims", true , true);
                return;
            }

            if (!Transmit(ctdwdisabletrims, true))
            {
                DisplayMessage("Failed to transmit byte for disable trims", true, true);
                return;
            }

            bytes_received = Receive(1, cPID_receive_timeout);
            if (bytes_received == null || bytes_received[0] != ctdwdisabletrims)
            {
                DisplayMessage("Failed to receive byte for disable trims", true, true);
                return;
            }

            bytes_received = Receive(1, cPID_receive_timeout);
            if (bytes_received == null)
            {
                DisplayMessage("Failed to receive disable trims", true, true);
                return;
            }

            Trace.WriteLine("TDW disable trims:");
            for (int i = 0; i < 8; i++)
            {
                tdwdisabletrims[i] = ((bytes_received[0] & (byte)Math.Pow(2, i)) == 0 ? false : true);
                Trace.WriteLine(tdwdisabletrims[i].ToString());
            }
            Trace.WriteLine("");
        }
        #endregion

        #region GetTDWGlobalTrims
        private void GetTDWGlobalTrims()
        {
            tdwglobaltrims = new BitArray(8, false);

            if (!ecuflags[cecuflags_supports_global_trims])
            {
                DisplayMessage("ECU does not support global trims", true, true);
                return;
            }

            if (!Transmit(ctdwglobaltrims, true))
            {
                DisplayMessage("Failed to transmit byte for global trims", true, true);
                return;
            }

            bytes_received = Receive(1, cPID_receive_timeout);
            if (bytes_received == null || bytes_received[0] != ctdwglobaltrims)
            {
                DisplayMessage("Failed to receive byte for global trims", true, true);
                return;
            }

            bytes_received = Receive(1, cPID_receive_timeout);
            if (bytes_received == null)
            {
                DisplayMessage("Failed to receive global trims", true, true);
                return;
            }

            Trace.WriteLine("TDW global trims:");
            for (int i = 0; i < 8; i++)
            {
                tdwglobaltrims[i] = ((bytes_received[0] & (byte)Math.Pow(2, i)) == 0 ? false : true);
                Trace.WriteLine(tdwglobaltrims[i].ToString());
            }
            Trace.WriteLine("");
        }
        #endregion

        #region IncreaseDecreaseTDWGlobalFuelTrim
        internal bool IncreaseDecreaseTDWGlobalFuelTrim(bool increase)
        {
            byte_to_receive_expected = false;
            if (increase)
            {
                byte_to_send = cGlobalFuelTrimIncrement;
            }
            else
            {
                byte_to_send = cGlobalFuelTrimDecrement;
            }
            send_byte = true;
            while (send_byte)
            {
                Application.DoEvents();
            }

            if (!byte_to_send_result || !byte_to_receive_result)
            {
                return false;
            }

            return true;
        }
        #endregion

        #region ResetTDWGlobalFuelTrim
        internal bool ResetTDWGlobalFuelTrim()
        {
            byte_to_receive_expected = false;
            byte_to_send = cGlobalFuelTrimClear;
            send_byte = true;
            while (send_byte)
            {
                Application.DoEvents();
            }

            if (!byte_to_send_result || !byte_to_receive_result)
            {
                return false;
            }

            return true;
        }
        #endregion

        #region EnableDisableTDWGlobalFuelTrim
        internal bool EnableDisableTDWGlobalFuelTrim(bool enable)
        {
            byte_to_receive_expected = false;
            if (enable)
            {
                byte_to_send = cGlobalFuelTrimEnable;
            }
            else
            {
                byte_to_send = cGlobalFuelTrimDisable;
            }
            send_byte = true;
            while (send_byte)
            {
                Application.DoEvents();
            }

            if (!byte_to_send_result || !byte_to_receive_result)
            {
                return false;
            }

            BitArray temp = TDWGlobalTrims;

            return true;
        }
        #endregion

        #region IncreaseDecreaseTDWGlobalVertCylTrim
        internal bool IncreaseDecreaseTDWGlobalVertCylTrim(bool increase)
        {
            byte_to_receive_expected = false;
            if (increase)
            {
                byte_to_send = cGlobalVertCylTrimIncrement;
            }
            else
            {
                byte_to_send = cGlobalVertCylTrimDecrement;
            }
            send_byte = true;
            while (send_byte)
            {
                Application.DoEvents();
            }

            if (!byte_to_send_result || !byte_to_receive_result)
            {
                return false;
            }

            return true;
        }
        #endregion

        #region ResetTDWGlobalVertCylTrim
        internal bool ResetTDWGlobalVertCylTrim()
        {
            byte_to_receive_expected = false;
            byte_to_send = cGlobalVertCylTrimClear;
            send_byte = true;
            while (send_byte)
            {
                Application.DoEvents();
            }

            if (!byte_to_send_result || !byte_to_receive_result)
            {
                return false;
            }

            return true;
        }
        #endregion

        #region EnableDisableTDWGlobalVertCylTrim
        internal bool EnableDisableTDWGlobalVertCylTrim(bool enable)
        {
            byte_to_receive_expected = false;
            if (enable)
            {
                byte_to_send = cGlobalVertCylTrimEnable;
            }
            else
            {
                byte_to_send = cGlobalVertCylTrimDisable;
            }
            send_byte = true;
            while (send_byte)
            {
                Application.DoEvents();
            }

            if (!byte_to_send_result || !byte_to_receive_result)
            {
                return false;
            }

            BitArray temp = TDWGlobalTrims;

            return true;
        }
        #endregion

        #region IncreaseDecreaseTDWGlobalSparkTrim
        internal bool IncreaseDecreaseTDWGlobalSparkTrim(bool increase)
        {
            byte_to_receive_expected = false;
            if (increase)
            {
                byte_to_send = cGlobalSparkTrimIncrement;
            }
            else
            {
                byte_to_send = cGlobalSparkTrimDecrement;
            }
            send_byte = true;
            while (send_byte)
            {
                Application.DoEvents();
            }

            if (!byte_to_send_result || !byte_to_receive_result)
            {
                return false;
            }

            return true;
        }
        #endregion

        #region ResetTDWGlobalSparkTrim
        internal bool ResetTDWGlobalSparkTrim()
        {
            byte_to_receive_expected = false;
            byte_to_send = cGlobalSparkTrimClear;
            send_byte = true;
            while (send_byte)
            {
                Application.DoEvents();
            }

            if (!byte_to_send_result || !byte_to_receive_result)
            {
                return false;
            }

            return true;
        }
        #endregion

        #region EnableDisableTDWGlobalSparkTrim
        internal bool EnableDisableTDWGlobalSparkTrim(bool enable)
        {
            byte_to_receive_expected = false;
            if (enable)
            {
                byte_to_send = cGlobalSparkTrimEnable;
            }
            else
            {
                byte_to_send = cGlobalSparkTrimDisable;
            }
            send_byte = true;
            while (send_byte)
            {
                Application.DoEvents();
            }

            if (!byte_to_send_result || !byte_to_receive_result)
            {
                return false;
            }

            BitArray temp = TDWGlobalTrims;

            return true;
        }
        #endregion

        #region EnableDisableTDWDisableFuelAirTempTrim
        internal bool EnableDisableTDWDisableFuelAirTempTrim(bool enable)
        {
            byte_to_receive_expected = false;
            if (enable)
            {
                byte_to_send = cDisableTrimsDisableFuelAirTempTrim;
            }
            else
            {
                byte_to_send = cDisableTrimsEnableFuelAirTempTrim;
            }
            send_byte = true;
            while (send_byte)
            {
                Application.DoEvents();
            }

            if (!byte_to_send_result || !byte_to_receive_result)
            {
                return false;
            }

            BitArray temp = TDWDisableTrims;

            return true;
        }
        #endregion

        #region EnableDisableTDWDisableFuelWaterTempTrim
        internal bool EnableDisableTDWDisableFuelWaterTempTrim(bool enable)
        {
            byte_to_receive_expected = false;
            if (enable)
            {
                byte_to_send = cDisableTrimsDisableFuelWaterTempTrim;
            }
            else
            {
                byte_to_send = cDisableTrimsEnableFuelWaterTempTrim;
            }
            send_byte = true;
            while (send_byte)
            {
                Application.DoEvents();
            }

            if (!byte_to_send_result || !byte_to_receive_result)
            {
                return false;
            }

            BitArray temp = TDWDisableTrims;

            return true;
        }
        #endregion

        #region EnableDisableTDWDisableFuelAltitudeTrim
        internal bool EnableDisableTDWDisableFuelAltitudeTrim(bool enable)
        {
            byte_to_receive_expected = false;
            if (enable)
            {
                byte_to_send = cDisableTrimsDisableFuelAltitudeTrim;
            }
            else
            {
                byte_to_send = cDisableTrimsEnableFuelAltitudeTrim;
            }
            send_byte = true;
            while (send_byte)
            {
                Application.DoEvents();
            }

            if (!byte_to_send_result || !byte_to_receive_result)
            {
                return false;
            }

            BitArray temp = TDWDisableTrims;

            return true;
        }
        #endregion

        #region GetTDWFlags
        private void GetTDWFlags()
        {
            tdwflags = new BitArray(8, false);

            if (!ecuflags[cecuflags_supports_tdwflags])
            {
                DisplayMessage("ECU does not support TDW flags", true, true);
                return;
            }
            
            if (!Transmit(ctdwflags, true))
            {
                DisplayMessage("Failed to transmit byte for TDW flags", true, true);
                return;
            }

            bytes_received = Receive(1, cPID_receive_timeout);
            if (bytes_received == null || bytes_received[0] != ctdwflags)
            {
                DisplayMessage("Failed to receive byte for TDW flags", true, true);
                return;
            }

            bytes_received = Receive(1, cPID_receive_timeout);
            if (bytes_received == null)
            {
                DisplayMessage("Failed to receive TDW flags", true, true);
                return;
            }

            Trace.WriteLine("TDW flags:");
            for (int i = 0; i < 8; i++)
            {
                tdwflags[i] = ((bytes_received[0] & (byte)Math.Pow(2, i)) == 0 ? false : true);
                Trace.WriteLine(tdwdisabletrims[i].ToString());
            }
            Trace.WriteLine("");
        }
        #endregion

        #region pc_NewAlarm
        private void pc_NewAlarm(SpPerfChart.PerfChart.AlarmSetpoint a)
        {
            lock (a)
            {
                rtbAlarms.AppendText(DateTime.Now.ToShortTimeString() + " SET " + a.PCName + " - " + a.Message + "\r\n");
                PlaySound(a.Sound, 1.0f);
                //rtbAlarms.SelectionStart = rtbAlarms.Text.Length;
                rtbAlarms.ScrollToCaret();
                //rtbAlarms.Refresh();
            }
        }
        #endregion

        #region pc_AlarmCleared
        private void pc_AlarmCleared(SpPerfChart.PerfChart.AlarmSetpoint a)
        {
            lock (a)
            {
                rtbAlarms.AppendText(DateTime.Now.ToShortTimeString() + " CLEARED " + a.PCName + " - " + a.Message + "\r\n");
                //rtbAlarms.SelectionStart = rtbAlarms.Text.Length;
                rtbAlarms.ScrollToCaret();
                //rtbAlarms.Refresh();
            }
        }
        #endregion

        #region pc_AlarmCompleted
        private void pc_AlarmCompleted(SpPerfChart.PerfChart.AlarmSetpoint a)
        {
            lock (a)
            {
                rtbAlarms.AppendText(DateTime.Now.ToShortTimeString() + " COMPLETED " + a.PCName + " - " + a.Message + "\r\n");
                //rtbAlarms.SelectionStart = rtbAlarms.Text.Length;
                rtbAlarms.ScrollToCaret();
                //rtbAlarms.Refresh();
            }
        }
        #endregion

        #region pc_AlarmDeleted
        private void pc_AlarmDeleted(SpPerfChart.PerfChart.AlarmSetpoint a)
        {
            DisplayMessage("Alarm " + a.PCName + " - " + a.Message + " deleted", true, true);
        }
        #endregion

        #region CheckForErrorsTimerTick
        private void CheckForErrorsTimerTick(object sender, EventArgs e)
        {
            check_for_errors_timer.Tick -= CheckForErrorsTimerTick;
            check_for_errors_timer.Stop();
            check_for_errors = true;
        }
        #endregion

        #region GetAllErrors
        // returns null if no errors were found (non-validated and validated)
        private String GetAllErrors(bool includesign)
        {
            String errors = "Errors found in the ECU:\r\n\r\nNon-validated (RAM):\r\n";

            // Non-validated (RAM)

            // non-validated input errors
            errors += "Input errors:\r\n";
            if (GetNonValidatedInputErrors())
            {
                errors += GetNonValidatedInputErrorsString(-1, includesign) + "\r\n";
            }
            else
            {
                errors += "Error retrieving non-validated input errors\r\n\r\n";
            }

            // non-validated output errors
            errors += "Output errors:\r\n";
            if (GetNonValidatedOutputErrors())
            {
                errors += GetNonValidatedOutputErrorsString(-1, includesign) + "\r\n";
            }
            else
            {
                errors += "Error retrieving non-validated output errors\r\n\r\n";
            }

            // non-validated functionality errors
            errors += "Functionality errors:\r\n";
            if (GetNonValidatedFunctionalityErrors())
            {
                errors += GetNonValidatedFunctionalityErrorsString(-1, includesign) + "\r\n";
            }
            else
            {
                errors += "Error retrieving non-validated functionality errors\r\n\r\n";
            }

            // validated (EEPROM)
            errors += "\r\nValidated (EEPROM):\r\n";

            // validated input errors
            errors += "Input errors:\r\n";
            if (GetValidatedInputErrors())
            {
                errors += GetValidatedInputErrorsString(-1, includesign) + "\r\n";
            }
            else
            {
                errors += "Error retrieving validated input errors\r\n\r\n";
            }

            // validated output errors
            errors += "Output errors:\r\n";
            if (GetValidatedOutputErrors())
            {
                errors += GetValidatedOutputErrorsString(-1, includesign) + "\r\n";
            }
            else
            {
                errors += "Error retrieving non-validated output errors\r\n\r\n";
            }

            // validated functionality errors
            errors += "Functionality errors:\r\n";
            if (GetValidatedFunctionalityErrors())
            {
                errors += GetValidatedFunctionalityErrorsString(-1, includesign) + "\r\n";
            }
            else
            {
                errors += "Error retrieving non-validated functionality errors\r\n\r\n";
            }
            
            // if there are no errors found then return null
            /*
            if (err_co_fun == 0x0 && err_co_inp == 0x0 && err_co_out == 0x0 && err_va_fun == 0x0 && err_va_inp == 0x0 && err_va_out == 0x0)
            {
                return null;
            }
            // else return the errors message
            else
            {
                return errors;
            }
            */

            return errors;
        }
        #endregion

        #region GetNonValidatedInputErrors
        private bool GetNonValidatedInputErrors()
        {
            bytes_received = null;
            err_co_inp = 0x0;

            if (!Transmit(cnonvalidated_input_errors, false))
            {
                MessageBox.Show("Failed to retrieve non-validated input errors", "Error", MessageBoxButtons.OK);
                return false;
            }

            bytes_received = Receive(2, cPID_receive_timeout);
            if (bytes_received == null || bytes_received[0] != cnonvalidated_input_errors)
            {
                MessageBox.Show("Failed to retrieve non-validated input errors", "Error", MessageBoxButtons.OK);
                return false;
            }

            err_co_inp = bytes_received[1];

            return GetNonValidatedInputErrorsSign();
        }
        #endregion

        #region GetNonValidatedInputErrorsString
        private String GetNonValidatedInputErrorsString(int startindex, bool includesign)
        {
            String nvie = "";
            int endindex = 0;

            if (startindex < 0 || startindex > 7)
            {
                startindex = 0;
                endindex = 8;
            }
            else
            {
                endindex = startindex + 1;
            }

            for (int i = startindex; i < endindex; i++)
            {
                if ((err_co_inp & (byte)Math.Pow(2,i)) > 0)
                {
                    nvie += err_co_va_inp_list[i] + "\r\n";

                    if (includesign)
                    {
                        nvie += "\t" + GetNonValidatedInputErrorsSignString(i);
                    }
                }
            }

            if (nvie == "")
            {
                nvie = "no errors found\r\n";
            }
            return nvie;
        }
        #endregion

        #region GetNonValidatedInputErrorsSign
        private bool GetNonValidatedInputErrorsSign()
        {
            bytes_received = null;
            se_co_inp = 0x0;

            if (!Transmit(cnonvalidated_input_errors_sign, false))
            {
                MessageBox.Show("Failed to retrieve non-validated input errors signs", "Error", MessageBoxButtons.OK);
                return false;
            }

            bytes_received = Receive(2, cPID_receive_timeout);
            if (bytes_received == null || bytes_received[0] != cnonvalidated_input_errors_sign)
            {
                MessageBox.Show("Failed to retrieve non-validated input errors signs", "Error", MessageBoxButtons.OK);
                return false;
            }

            se_co_inp = bytes_received[1];
            return true;
        }
        #endregion

        #region GetNonValidatedInputErrorsSignString
        // pass -1 to get all the bits or pass certain bit - 0 based!
        private String GetNonValidatedInputErrorsSignString(int startindex)
        {
            String nvies = "";
            int result = -1, endindex = 0;

            if (startindex < 0 || startindex > 7)
            {
                startindex = 0;
                endindex = 8;
            }
            else
            {
                endindex = startindex + 1;
            }

            for (int i = startindex; i < endindex; i++)
            {
                result = (se_co_inp & (byte)Math.Pow(2, i));

                if (result == 0x0)
                {
                    nvies += se_co_va_inp_list[(i * 2) + 1] + "\r\n";
                }
                else
                {
                    nvies += se_co_va_inp_list[i * 2] + "\r\n";
                }
            }

            return nvies;
        }
        #endregion

        #region GetNonValidatedOutputErrors
        private bool GetNonValidatedOutputErrors()
        {
            bytes_received = null;
            err_co_out = 0x0;

            if (!Transmit(cnonvalidated_output_errors, false))
            {
                MessageBox.Show("Failed to retrieve non-validated output errors", "Error", MessageBoxButtons.OK);
                return false;
            }

            bytes_received = Receive(2, cPID_receive_timeout);
            if (bytes_received == null || bytes_received[0] != cnonvalidated_output_errors)
            {
                MessageBox.Show("Failed to retrieve non-validated output errors", "Error", MessageBoxButtons.OK);
                return false;
            }

            err_co_out = bytes_received[1];

            return GetNonValidatedOutputErrorsSign();
        }
        #endregion

        #region GetNonValidatedOutputErrorsString
        private String GetNonValidatedOutputErrorsString(int startindex, bool includesign)
        {
            String nvoe = "";
            int endindex = 0;

            if (startindex < 0 || startindex > 7)
            {
                startindex = 0;
                endindex = 8;
            }
            else
            {
                endindex = startindex + 1;
            }

            for (int i = startindex; i < endindex; i++)
            {
                if ((err_co_out & (byte)Math.Pow(2, i)) > 0)
                {
                    nvoe += err_co_va_out_list[i] + "\r\n";

                    if (includesign)
                    {
                        nvoe += "\t" + GetNonValidatedOutputErrorsSignString(i);
                    }
                }
            }

            if (nvoe == "")
            {
                nvoe = "no errors found\r\n";
            }
            return nvoe;
        }
        #endregion

        #region GetNonValidatedOutputErrorsSign
        private bool GetNonValidatedOutputErrorsSign()
        {
            bytes_received = null;
            se_co_out = 0x0;

            if (!Transmit(cnonvalidated_output_errors_sign, false))
            {
                MessageBox.Show("Failed to retrieve non-validated output errors signs", "Error", MessageBoxButtons.OK);
                return false;
            }

            bytes_received = Receive(2, cPID_receive_timeout);
            if (bytes_received == null || bytes_received[0] != cnonvalidated_output_errors_sign)
            {
                MessageBox.Show("Failed to retrieve non-validated output errors signs", "Error", MessageBoxButtons.OK);
                return false;
            }

            se_co_out = bytes_received[1];
            return true;
        }
        #endregion

        #region GetNonValidatedOutputErrorsSignString
        private String GetNonValidatedOutputErrorsSignString(int startindex)
        {
            String nvoes = "";
            int result = -1, endindex = 0;

            if (startindex < 0 || startindex > 7)
            {
                startindex = 0;
                endindex = 8;
            }
            else
            {
                endindex = startindex + 1;
            }

            for (int i = startindex; i < endindex; i++)
            {
                result = (se_co_out & (byte)Math.Pow(2, i));

                if (result == 0x0)
                {
                    nvoes += se_co_va_out_list[(i * 2) + 1] + "\r\n";
                }
                else
                {
                    nvoes += se_co_va_out_list[i * 2] + "\r\n";
                }
            }

            return nvoes;
        }
        #endregion

        #region GetNonValidatedFunctionalityErrors
        private bool GetNonValidatedFunctionalityErrors()
        {
            bytes_received = null;
            err_co_fun = 0x0;

            if (!Transmit(cnonvalidated_functionality_errors, false))
            {
                MessageBox.Show("Failed to retrieve non-validated functionality errors", "Error", MessageBoxButtons.OK);
                return false;
            }

            bytes_received = Receive(2, cPID_receive_timeout);
            if (bytes_received == null || bytes_received[0] != cnonvalidated_functionality_errors)
            {
                MessageBox.Show("Failed to retrieve non-validated functionality errors", "Error", MessageBoxButtons.OK);
                return false;
            }

            err_co_fun = bytes_received[1];

            return GetNonValidatedFunctionalityErrorsSign();
        }
        #endregion

        #region GetNonValidatedFunctionalityErrorsString
        private String GetNonValidatedFunctionalityErrorsString(int startindex, bool includesign)
        {
            String nvfe = "";
            int endindex = 0;

            if (startindex < 0 || startindex > 7)
            {
                startindex = 0;
                endindex = 8;
            }
            else
            {
                endindex = startindex + 1;
            }

            for (int i = startindex; i < endindex; i++)
            {
                if ((err_co_fun & (byte)Math.Pow(2, i)) > 0)
                {
                    nvfe += err_co_va_fun_list[i] + "\r\n";

                    if (includesign)
                    {
                        nvfe += "\t" + GetNonValidatedFunctionalityErrorsSignString(i);
                    }
                }
            }

            if (nvfe == "")
            {
                nvfe = "no errors found\r\n";
            }
            return nvfe;
        }
        #endregion

        #region GetNonValidatedFunctionalityErrorsSign
        private bool GetNonValidatedFunctionalityErrorsSign()
        {
            bytes_received = null;
            se_co_fun = 0x0;

            if (!Transmit(cnonvalidated_functionality_errors_sign, false))
            {
                MessageBox.Show("Failed to retrieve non-validated functionality errors signs", "Error", MessageBoxButtons.OK);
                return false;
            }

            bytes_received = Receive(2, cPID_receive_timeout);
            if (bytes_received == null || bytes_received[0] != cnonvalidated_functionality_errors_sign)
            {
                MessageBox.Show("Failed to retrieve non-validated functionality errors signs", "Error", MessageBoxButtons.OK);
                return false;
            }

            se_co_fun = bytes_received[1];
            return true;
        }
        #endregion

        #region GetNonValidatedFunctionalityErrorsSignString
        private String GetNonValidatedFunctionalityErrorsSignString(int startindex)
        {
            String nvfes = "";
            int result = -1, endindex = 0;

            if (startindex < 0 || startindex > 7)
            {
                startindex = 0;
                endindex = 8;
            }
            else
            {
                endindex = startindex + 1;
            }

            for (int i = startindex; i < endindex; i++)
            {
                result = (se_co_fun & (byte)Math.Pow(2, i));

                if (result == 0x0)
                {
                    nvfes += se_co_va_fun_list[(i * 2) + 1] + "\r\n";
                }
                else
                {
                    nvfes += se_co_va_fun_list[i * 2] + "\r\n";
                }
            }

            return nvfes;
        }
        #endregion

        #region GetValidatedInputErrors
        private bool GetValidatedInputErrors()
        {
            bytes_received = null;
            err_va_inp = 0x0;

            if (!Transmit(cvalidated_input_errors, false))
            {
                MessageBox.Show("Failed to retrieve validated input errors", "Error", MessageBoxButtons.OK);
                return false;
            }

            bytes_received = Receive(2, cPID_receive_timeout);
            if (bytes_received == null || bytes_received[0] != cvalidated_input_errors)
            {
                MessageBox.Show("Failed to retrieve validated input errors", "Error", MessageBoxButtons.OK);
                return false;
            }

            err_va_inp = bytes_received[1];

            return GetValidatedInputErrorsSign();
        }
        #endregion

        #region GetValidatedInputErrorsString
        private String GetValidatedInputErrorsString(int startindex, bool includesign)
        {
            String vie = "";
            int endindex = 0;

            if (startindex < 0 || startindex > 7)
            {
                startindex = 0;
                endindex = 8;
            }
            else
            {
                endindex = startindex + 1;
            }

            for (int i = startindex; i < endindex; i++)
            {
                if ((err_va_inp & (byte)Math.Pow(2, i)) > 0)
                {
                    vie += err_co_va_inp_list[i] + "\r\n";

                    if (includesign)
                    {
                        vie += "\t" + GetValidatedInputErrorsSignString(i);
                    }
                }
            }

            if (vie == "")
            {
                vie = "no errors found\r\n";
            }
            return vie;
        }
        #endregion

        #region GetValidatedInputErrorsSign
        private bool GetValidatedInputErrorsSign()
        {
            bytes_received = null;
            se_va_inp = 0x0;

            if (!Transmit(cvalidated_input_errors_sign, false))
            {
                MessageBox.Show("Failed to retrieve validated input errors signs", "Error", MessageBoxButtons.OK);
                return false;
            }

            bytes_received = Receive(2, cPID_receive_timeout);
            if (bytes_received == null || bytes_received[0] != cvalidated_input_errors_sign)
            {
                MessageBox.Show("Failed to retrieve validated input errors signs", "Error", MessageBoxButtons.OK);
                return false;
            }

            se_va_inp = bytes_received[1];
            return true;
        }
        #endregion

        #region GetValidatedInputErrorsSignString
        private String GetValidatedInputErrorsSignString(int startindex)
        {
            String vies = "";
            int result = -1, endindex = 0;

            if (startindex < 0 || startindex > 7)
            {
                startindex = 0;
                endindex = 8;
            }
            else
            {
                endindex = startindex + 1;
            }

            for (int i = startindex; i < endindex; i++)
            {
                result = (se_va_inp & (byte)Math.Pow(2, i));

                if (result == 0x0)
                {
                    vies += se_co_va_inp_list[(i * 2) + 1] + "\r\n";
                }
                else
                {
                    vies += se_co_va_inp_list[i * 2] + "\r\n";
                }
            }

            return vies;
        }
        #endregion

        #region GetValidatedOutputErrors
        private bool GetValidatedOutputErrors()
        {
            bytes_received = null;
            err_va_out = 0x0;

            if (!Transmit(cvalidated_output_errors, false))
            {
                MessageBox.Show("Failed to retrieve validated output errors", "Error", MessageBoxButtons.OK);
                return false;
            }

            bytes_received = Receive(2, cPID_receive_timeout);
            if (bytes_received == null || bytes_received[0] != cvalidated_output_errors)
            {
                MessageBox.Show("Failed to retrieve validated output errors", "Error", MessageBoxButtons.OK);
                return false;
            }

            err_va_out = bytes_received[1];

            return GetValidatedOutputErrorsSign();
        }
        #endregion

        #region GetValidatedOutputErrorsString
        private String GetValidatedOutputErrorsString(int startindex, bool includesign)
        {
            String voe = "";
            int endindex = 0;

            if (startindex < 0 || startindex > 7)
            {
                startindex = 0;
                endindex = 8;
            }
            else
            {
                endindex = startindex + 1;
            }

            for (int i = startindex; i < endindex; i++)
            {
                if ((err_va_out & (byte)Math.Pow(2, i)) > 0)
                {
                    voe += err_co_va_out_list[i] + "\r\n";

                    if (includesign)
                    {
                        voe += "\t" + GetValidatedOutputErrorsSignString(i);
                    }
                }
            }

            if (voe == "")
            {
                voe = "no errors found\r\n";
            }
            return voe;
        }
        #endregion

        #region GetValidatedOutputErrorsSign
        private bool GetValidatedOutputErrorsSign()
        {
            bytes_received = null;
            se_va_out = 0x0;

            if (!Transmit(cvalidated_output_errors_sign, false))
            {
                MessageBox.Show("Failed to retrieve validated output errors signs", "Error", MessageBoxButtons.OK);
                return false;
            }

            bytes_received = Receive(2, cPID_receive_timeout);
            if (bytes_received == null || bytes_received[0] != cvalidated_output_errors_sign)
            {
                MessageBox.Show("Failed to retrieve validated output errors signs", "Error", MessageBoxButtons.OK);
                return false;
            }

            se_va_out = bytes_received[1];
            return true;
        }
        #endregion

        #region GetValidatedOutputErrorsSignString
        private String GetValidatedOutputErrorsSignString(int startindex)
        {
            String voes = "";
            int result = -1, endindex = 0;

            if (startindex < 0 || startindex > 7)
            {
                startindex = 0;
                endindex = 8;
            }
            else
            {
                endindex = startindex + 1;
            }

            for (int i = startindex; i < endindex; i++)
            {
                result = (se_va_out & (byte)Math.Pow(2, i));

                if (result == 0x0)
                {
                    voes += se_co_va_out_list[(i * 2) + 1] + "\r\n";
                }
                else
                {
                    voes += se_co_va_out_list[i * 2] + "\r\n";
                }
            }

            return voes;
        }
        #endregion

        #region GetValidatedFunctionalityErrors
        private bool GetValidatedFunctionalityErrors()
        {
            bytes_received = null;
            err_va_fun = 0x0;

            if (!Transmit(cvalidated_functionality_errors, false))
            {
                MessageBox.Show("Failed to retrieve validated functionality errors", "Error", MessageBoxButtons.OK);
                return false;
            }

            bytes_received = Receive(2, cPID_receive_timeout);
            if (bytes_received == null || bytes_received[0] != cvalidated_functionality_errors)
            {
                MessageBox.Show("Failed to retrieve validated functionality errors", "Error", MessageBoxButtons.OK);
                return false;
            }

            err_va_fun = bytes_received[1];

            return GetValidatedFunctionalityErrorsSign();
        }
        #endregion

        #region GetValidatedFunctionalityErrorsString
        private String GetValidatedFunctionalityErrorsString(int startindex, bool includesign)
        {
            String vfe = "";
            int endindex = 0;

            if (startindex < 0 || startindex > 7)
            {
                startindex = 0;
                endindex = 8;
            }
            else
            {
                endindex = startindex + 1;
            }

            for (int i = startindex; i < endindex; i++)
            {
                if ((err_va_fun & (byte)Math.Pow(2, i)) > 0)
                {
                    vfe += err_co_va_fun_list[i] + "\r\n";

                    if (includesign)
                    {
                        vfe += "\t" + GetValidatedFunctionalityErrorsSignString(i);
                    }
                }
            }

            if (vfe == "")
            {
                vfe = "no errors found\r\n";
            }
            return vfe;
        }
        #endregion

        #region GetValidatedFunctionalityErrorsSign
        private bool GetValidatedFunctionalityErrorsSign()
        {
            bytes_received = null;
            se_va_fun = 0x0;

            if (!Transmit(cvalidated_functionality_errors_sign, false))
            {
                MessageBox.Show("Failed to retrieve validated functionality errors signs", "Error", MessageBoxButtons.OK);
                return false;
            }

            bytes_received = Receive(2, cPID_receive_timeout);
            if (bytes_received == null || bytes_received[0] != cvalidated_functionality_errors_sign)
            {
                MessageBox.Show("Failed to retrieve validated functionality errors signs", "Error", MessageBoxButtons.OK);
                return false;
            }

            se_va_fun = bytes_received[1];
            return true;
        }
        #endregion

        #region GetValidatedFunctionalityErrorsSignString
        private String GetValidatedFunctionalityErrorsSignString(int startindex)
        {
            String vfes = "";
            int result = -1, endindex = 0;

            if (startindex < 0 || startindex > 7)
            {
                startindex = 0;
                endindex = 8;
            }
            else
            {
                endindex = startindex + 1;
            }

            for (int i = startindex; i < endindex; i++)
            {
                result = (se_va_fun & (byte)Math.Pow(2, i));

                if (result == 0x0)
                {
                    vfes += se_co_va_fun_list[(i * 2) + 1] + "\r\n";
                }
                else
                {
                    vfes += se_co_va_fun_list[i * 2] + "\r\n";
                }
            }

            return vfes;
        }
        #endregion

        #region UpdateValues
        private void UpdateValues()
        {
            Trace.WriteLine("Update values start");

            while (!form_closing)
            {
                try
                {
                    if (master_poll_enable)
                    {
                        //Application.DoEvents();
                        #region Battery voltage
                        if (master_poll_enable)
                        {
                            tbBatteryVoltage.Text = battery_voltage.ToString("F2") + "Vdc";
                            //DisplayMessage("Battery voltage: " + battery_voltage.ToString("F2") + "Vdc", false, true);
                        }
                        #endregion

                        #region Throttle angle
                        if (master_poll_enable)
                        {
                            tbThrottleAngle.Text = throttle_angle.ToString("F2") + Convert.ToChar(0xB0);
                            //DisplayMessage("Throttle angle: " + throttle_angle.ToString("F2") + Convert.ToChar(0xB0), false, true);
                        }
                        #endregion

                        #region MAP
                        if (master_poll_enable)
                        {
                            tbMAP.Text = manifold_absolute_pressure.ToString("F2") + "mmHg";
                            //DisplayMessage("Manifold absolute pressure: " + manifold_absolute_pressure.ToString("F2") + "mmHg", false, true);
                        }
                        #endregion

                        #region Air temp
                        if (master_poll_enable)
                        {
                            tbAirTemp.Text = air_temp.ToString("F2") + Convert.ToChar(0xB0) + "C" + "\t" + CelsiusToFahrenheit(air_temp).ToString("F2") + Convert.ToChar(0xB0) + "F";
                            //DisplayMessage("Air temp: " + air_temp.ToString("F2") + Convert.ToChar(0xB0) + "C" + "\t" + CelsiusToFahrenheit(air_temp).ToString("F2") + Convert.ToChar(0xB0) + "F", false, true);
                        }
                        #endregion

                        #region Water temp
                        if (master_poll_enable)
                        {
                            tbWaterTemp.Text = water_temp.ToString("F2") + Convert.ToChar(0xB0) + "C" + "\t" + CelsiusToFahrenheit(water_temp).ToString("F2") + Convert.ToChar(0xB0) + "F";
                            //DisplayMessage("Water temp: " + water_temp.ToString("F2") + Convert.ToChar(0xB0) + "C" + "\t" + CelsiusToFahrenheit(water_temp).ToString("F2") + Convert.ToChar(0xB0) + "F", false, true);
                        }
                        #endregion

                        #region Trimmer position
                        if (master_poll_enable)
                        {
                            tbTrimmerPos.Text = trimmer_position.ToString("F0");
                            //DisplayMessage("Trimmer position: " + trimmer_position.ToString("F0"), false, true);
                        }
                        #endregion

                        #region RPM
                        if (master_poll_enable)
                        {
                            tbRPM.Text = rpm.ToString("F2");
                            //DisplayMessage("RPM: " + rpm.ToString("F2"), false, true);
                        }
                        #endregion

                        #region Injector duration
                        if (master_poll_enable)
                        {
                            tbInjectorDuration.Text = injector_duration.ToString("F2") + "ms";
                            //DisplayMessage("Injector Duration: " + injector_duration.ToString("F2") + "ms", false, true);
                        }
                        #endregion

                        #region Ignition advance
                        if (master_poll_enable)
                        {
                            tbIgnitionAdvance.Text = ignition_advance.ToString("F2") + Convert.ToChar(0xB0);
                            //DisplayMessage("Ignition Advance: " + ignition_advance.ToString("F2") + Convert.ToChar(0xB0), false, true);
                        }
                        #endregion

                        #region Fuel trim raw
                        if (master_poll_enable)
                        {
                            tbFuelTrim.Text = fuel_trim.ToString("X2");
                            //DisplayMessage("Fuel trim: " + fuel_trim.ToString("X2"));
                        }
                        #endregion

                        #region Fuel trim power on
                        if (master_poll_enable)
                        {
                            tbFuelTrimPowerOn.Text = fuel_trim_power_on.ToString("X2");
                            //DisplayMessage("Fuel trim: " + fuel_trim.ToString("X2"));
                        }
                        #endregion

                        #region Start enrichment
                        if (master_poll_enable)
                        {
                            tbStartEnrichment.Text = start_enrichment.ToString("X2");
                            //DisplayMessage("Start enrichment: " + start_enrichment.ToString("X2"));
                        }
                        #endregion

                        #region Spare 1
                        if (master_poll_enable)
                        {
                            tbSpare1.Text = spare1.ToString("X2");
                            //DisplayMessage("Spare 1: " + spare1.ToString("X2"));
                        }
                        #endregion

                        #region Spare 2
                        if (master_poll_enable)
                        {
                            tbSpare2.Text = spare2.ToString("X2");
                            //DisplayMessage("Spare 2: " + spare2.ToString("X2"));
                        }
                        #endregion

                        /*
                        #region Spare 2
                        if (master_poll_enable)
                        {
                            //tbSpare2.Text = icurrent_spark_advance_word.ToString("X2");
                            tbSpare2.Text = ((icurrent_spark_advance_word / 2.0f) / 256).ToString("F2");
                            //DisplayMessage("Spare 2: " + spare2.ToString("X2"));
                        }
                        #endregion
                        */

                        //DisplayMessage("", false, true);
                    }
                }
                catch (Exception e)
                {
                    Trace.WriteLine("Update values exception: " + e.Message);
                }
            }

            Trace.WriteLine("Update values stop");
        }
        #endregion

        #region UpdatePCs
        #region UpdateBatteryVoltagePC
        private void UpdateBatteryVoltagePC()
        {
            if (!master_poll_enable)
            {
                return;
            }

            pc_battery_voltage = (battery_voltage / max_battery_voltage) * 100;
            if (pc_battery_voltage > 100)
            {
                pc_battery_voltage = 100;
            }
            else if (pc_battery_voltage < 0)
            {
                pc_battery_voltage = 0;
            }
            pcBatteryVoltage.AddValue((decimal)pc_battery_voltage);
        }
        #endregion

        #region UpdateThrottleAnglePC
        private void UpdateThrottleAnglePC()
        {
            if (!master_poll_enable)
            {
                return;
            }
            
            pc_throttle_angle = (throttle_angle / max_throttle_angle) * 100;
            if (pc_throttle_angle > 100)
            {
                pc_throttle_angle = 100;
            }
            else if (pc_throttle_angle < 0)
            {
                pc_throttle_angle = 0;
            }
            pcThrottleAngle.AddValue((decimal)pc_throttle_angle);
        }
        #endregion

        #region UpdateMAPPC
        private void UpdateMAPPC()
        {
            if (!master_poll_enable)
            {
                return;
            }

            pc_manifold_absolute_pressure = (manifold_absolute_pressure / max_MAP) * 100;
            if (pc_manifold_absolute_pressure > 100)
            {
                pc_manifold_absolute_pressure = 100;
            }
            else if (pc_manifold_absolute_pressure < 0)
            {
                pc_manifold_absolute_pressure = 0;
            }
            pcMAP.AddValue((decimal)pc_manifold_absolute_pressure);
        }
        #endregion

        #region UpdateAirTempPC
        private void UpdateAirTempPC()
        {
            if (!master_poll_enable)
            {
                return;
            }
            
            pc_air_temp = (air_temp / max_air_temp) * 100;
            if (pc_air_temp > 100)
            {
                pc_air_temp = 100;
            }
            else if (pc_air_temp < 0)
            {
                pc_air_temp = 0;
            }
            pcAirTemp.AddValue((decimal)pc_air_temp);
        }
        #endregion

        #region UpdateWaterTempPC
        private void UpdateWaterTempPC()
        {
            if (!master_poll_enable)
            {
                return;
            }

            pc_water_temp = (water_temp / max_water_temp) * 100;
            if (pc_water_temp > 100)
            {
                pc_water_temp = 100;
            }
            else if (pc_water_temp < 0)
            {
                pc_water_temp = 0;
            }
            pcWaterTemp.AddValue((decimal)pc_water_temp);
        }
        #endregion

        #region UpdateRPMPC
        private void UpdateRPMPC()
        {
            if (!master_poll_enable)
            {
                return;
            }
            
            pc_rpm = (rpm / max_rpm) * 100;
            if (pc_rpm > 100)
            {
                pc_rpm = 100;
            }
            else if (pc_rpm < 0)
            {
                pc_rpm = 0;
            }
            pcRPM.AddValue((decimal)pc_rpm);
        }
        #endregion

        #region UpdateInjectorDurationPC
        private void UpdateInjectorDurationPC()
        {
            if (!master_poll_enable)
            {
                return;
            }
            
            pc_injector_duration = (injector_duration / max_injector_duration) * 100;
            if (pc_injector_duration > 100)
            {
                pc_injector_duration = 100;
            }
            else if (pc_injector_duration < 0)
            {
                pc_injector_duration = 0;
            }
            pcInjectorDuration.AddValue((decimal)pc_injector_duration);
        }
        #endregion

        #region UpdateIgnitionAdvancePC
        private void UpdateIgnitionAdvancePC()
        {
            if (!master_poll_enable)
            {
                return;
            }
            
            pc_ignition_advance = (ignition_advance / max_ignition_advance) * 100;
            if (pc_ignition_advance > 100)
            {
                pc_ignition_advance = 100;
            }
            else if (pc_ignition_advance < 0)
            {
                pc_ignition_advance = 0;
            }
            pcIgnitionAdvance.AddValue((decimal)pc_ignition_advance);
        }
        #endregion
        #endregion

        #region UpdatePCMaxValues
        private void UpdatePCMaxValues()
        {
            gbAirTempGraph.Text = (String)pcAirTemp.Tag + " (Max " + max_air_temp.ToString("F2") + Convert.ToChar(0xB0) + "C)";
            gbWaterTempGraph.Text = (String)pcWaterTemp.Tag + " (Max " + max_water_temp.ToString("F2") + Convert.ToChar(0xB0) + "C)";
            gbBatteryVoltageGraph.Text = (String)pcBatteryVoltage.Tag + " (Max " + max_battery_voltage.ToString("F2") + "Vdc)";
            gbIgnitionAdvanceGraph.Text = (String)pcIgnitionAdvance.Tag + " (Max " + max_ignition_advance.ToString("F2") + Convert.ToChar(0xB0) + ")";
            gbInjectorDuationGraph.Text = (String)pcInjectorDuration.Tag + " (Max " + max_injector_duration.ToString("F2") + "ms)";
            gbMAPGraph.Text = (String)pcMAP.Tag + " (Max " + max_MAP.ToString("F2") + "mmHg)";
            gbRPMGraph.Text = (String)pcRPM.Tag + " (Max " + max_rpm.ToString("F2") + ")";
            gbThrottleAngleGraph.Text = (String)pcThrottleAngle.Tag + " (Max " + max_throttle_angle.ToString("F2") + Convert.ToChar(0xB0) + ")";

            pcBatteryVoltage.Scaling = (decimal)max_battery_voltage;
            pcIgnitionAdvance.Scaling = (decimal)max_ignition_advance;
            pcInjectorDuration.Scaling = (decimal)max_injector_duration;
            pcRPM.Scaling = (decimal)max_rpm;
            pcWaterTemp.Scaling = (decimal)max_water_temp;
            pcAirTemp.Scaling = (decimal)max_air_temp;
            pcMAP.Scaling = (decimal)max_MAP;
            pcThrottleAngle.Scaling = (decimal)max_throttle_angle;
        }
        #endregion

        #region ReceivingData
        private void ReceivingData(bool value)
        {
            // show down arrow
            tsslComms.Text = (value ? Convert.ToChar(0x2193).ToString() : "");
            this.Update();
        }
        #endregion

        #region SendingData
        private void SendingData(bool value)
        {
            // show up arrow
            tsslComms.Text = (value ? Convert.ToChar(0x2191).ToString() : "");
            this.Update();
        }
        #endregion

        #region DisplayDialog
        internal void DisplayDialog(String message, bool show)
        {
            if (show)
            {
                dialog.lblMessage.Text = message;
                dialog.Show();
            }
            else
            {
                dialog.lblMessage.Text = "";
                dialog.Hide();
            }
        }
        #endregion

        #region DisplayMessage
        private void DisplayMessage(String message, bool addtostatus, bool addtrace)
        {
            if (addtostatus)
            {
                rtbStatus.AppendText(message + "\r\n");
                rtbStatus.ScrollToCaret();
            }

            if (addtrace)
            {
                Trace.WriteLine(message);
            }
        }
        #endregion

        #region Get EXE Version
        private String GetEXEVersion()
        {
            FileVersionInfo fi = FileVersionInfo.GetVersionInfo(Application.ExecutablePath);
            String fileversion = fi.FileVersion.Replace(',', '.');
            fileversion = fileversion.Replace(" ", "");
            return fileversion;
        }
        #endregion

        #region rtbStatus_MouseDoubleClick
        private void rtbStatus_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            rtbStatus.Clear();
        }
        #endregion

        #region rtbAlarms_MouseDoubleClick
        private void rtbAlarms_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            rtbAlarms.Clear();
        }
        #endregion

        #region nudPollSpeed_ValueChanged
        private void nudPollSpeed_ValueChanged(object sender, EventArgs e)
        {
            //poll_ecu_data_loop_sleep = (int)nudPollSpeed.Value;
            //DisplayMessage("Update speed changed to " + poll_ecu_data_loop_sleep + "ms", true, true);
            if (this.Visible)
            {
                DisplayMessage("Update speed changed to " + (int)nudPollSpeed.Value + "ms", true, true);
            }
            int newvalue = (int)nudPollSpeed.Value;
            cfgfileentries[ccfgpollgraphspeed][0] = newvalue.ToString();

            foreach (SpPerfChart.PerfChart pc in perfcharts)
            {
                if (pccbs[perfcharts.IndexOf(pc)].Checked)
                {
                    SetNewPCTimerInterval(pc, newvalue);
                }
            }
        }
        #endregion

        #region LoggingCBsCheckChanged
        private void LoggingCBsCheckChanged(object sender, EventArgs e)
        {
            if (pccbs.IndexOf((CheckBox)sender) >= perfcharts.Count)
            {
                return;
            }

            SpPerfChart.PerfChart pc = perfcharts[pccbs.IndexOf((CheckBox)sender)];
            SetNewPCTimerMode(pc, true);
        }
        #endregion

        #region CelsiusToFahrenheit
        private static float CelsiusToFahrenheit(float c)
        {
            return ((9.0f / 5.0f) * c) + 32;
        }
        #endregion

        #region SetSplittersCMSs
        private void SetSplittersCMSs()
        {
            foreach (SplitContainer sc in splitters)
            {
                ContextMenuStrip cms = new ContextMenuStrip();

                ToolStripMenuItem reset = new ToolStripMenuItem(ccmsreset);
                reset.Tag = sc;
                reset.ToolTipText = "Resets the splitter to as designed location";
                reset.Click += new EventHandler(CMSSplitters_Click);
                cms.Items.Add(reset);

                ToolStripMenuItem resetall = new ToolStripMenuItem(ccmsresetall);
                resetall.Tag = null;
                resetall.ToolTipText = "Resets all the splitters to as designed location";
                resetall.Click += new EventHandler(CMSSplitters_Click);
                cms.Items.Add(resetall);

                sc.ContextMenuStrip = cms;
            }
        }
        #endregion

        #region CMSSplitters_Click
        private void CMSSplitters_Click(object sender, EventArgs e)
        {
            ToolStripMenuItem tsmi = (ToolStripMenuItem)sender;
            SplitContainer sc = null;
            if (tsmi.Tag != null)
            {
                sc = (SplitContainer)tsmi.Tag;
            }
            String tsmitext = tsmi.Text;

            if (tsmitext == ccmsreset)
            {
                sc.SplitterDistance = splitter_defaults[splitters.IndexOf(sc)];
            }
            else if (tsmitext == ccmsresetall)
            {
                foreach (SplitContainer sc2 in splitters)
                {
                    sc2.SplitterDistance = splitter_defaults[splitters.IndexOf(sc2)];
                }
            }
        }
        #endregion

        #region gbGraphs_Leave
        private void gbGraphs_Leave(object sender, EventArgs e)
        {
            this.Focus();
        }
        #endregion

        #region PCsMouseEnter
        private void PCsMouseEnter(object sender, EventArgs e)
        {
            SpPerfChart.PerfChart pc = (SpPerfChart.PerfChart)sender;
            pc.Focus();
        }
        #endregion

        #region SetPCsTimerMode
        private void SetPCsTimerMode(bool updatecfg)
        {
            foreach (SpPerfChart.PerfChart pc in perfcharts)
            {
                SetNewPCTimerMode(pc, updatecfg);
            }
        }
        #endregion

        #region SetPCCMSs
        private void SetPCCMSs()
        {
            foreach (SpPerfChart.PerfChart pc in perfcharts)
            {
                ContextMenuStrip cms = new ContextMenuStrip();
                cms.Tag = pc;

                ToolStripMenuItem changename = new ToolStripMenuItem(ccmschangename);
                changename.ToolTipText = "Changes the name displayed for the graph\r\nClick to change name";
                changename.Click += new EventHandler(PCCMS_Click);
                changename.Tag = pc;
                cms.Items.Add(changename);

                ToolStripMenuItem max = new ToolStripMenuItem(ccmssetmax);
                max.ToolTipText = "Sets the maximum value allowed for this graph\r\nClick to change maximum value";
                max.Click += new EventHandler(PCCMS_Click);
                max.Tag = pc;
                cms.Items.Add(max);

                ToolStripMenuItem cleargraph = new ToolStripMenuItem(ccmscleargraph);
                cleargraph.ToolTipText = "clears all the entries from the graph";
                cleargraph.Click += new EventHandler(PCCMS_Click);
                cleargraph.Tag = pc;
                cms.Items.Add(cleargraph);

                ToolStripMenuItem takepicture = new ToolStripMenuItem(ccmstakepicture);
                takepicture.ToolTipText = "Takes a picture of the graph in BMP format";
                takepicture.Click += new EventHandler(PCCMS_Click);
                takepicture.Tag = pc;
                cms.Items.Add(takepicture);

                ToolStripSeparator tss1 = new ToolStripSeparator();
                cms.Items.Add(tss1);

                ToolStripMenuItem chartmode = new ToolStripMenuItem(ccmschartmode);
                chartmode.ToolTipText = "Selects the graph's mode (absolute or relative)";
                chartmode.Tag = pc;

                ToolStripMenuItem chartmodeabsolute = new ToolStripMenuItem(ccmschartmodeabsolute);
                chartmodeabsolute.ToolTipText = "Absolute graph mode (0-100)\r\nClick to select this graph mode";
                chartmodeabsolute.Click += new EventHandler(PCCMS_Click);
                chartmodeabsolute.Tag = pc;
                chartmode.DropDownItems.Add(chartmodeabsolute);

                ToolStripMenuItem chartmoderelative = new ToolStripMenuItem(ccmschartmoderelative);
                chartmoderelative.ToolTipText = "Relative graph mode\r\nClick to select this graph mode";
                chartmoderelative.Click += new EventHandler(PCCMS_Click);
                chartmoderelative.Tag = pc;
                chartmode.DropDownItems.Add(chartmoderelative);

                cms.Items.Add(chartmode);

                ToolStripSeparator tss2 = new ToolStripSeparator();
                cms.Items.Add(tss2);

                ToolStripMenuItem avglineviewable = new ToolStripMenuItem(ccmsavglineviewable);
                avglineviewable.ToolTipText = "Displays a line that is the average of all the visible values currently shown on the chart\r\nClick to enable/disable";
                avglineviewable.Click += new EventHandler(PCCMS_Click);
                avglineviewable.Tag = pc;
                cms.Items.Add(avglineviewable);

                ToolStripMenuItem avglinetotal = new ToolStripMenuItem(ccmsavglinetotal);
                avglinetotal.ToolTipText = "Displays a line that is the average of all the values in chart\r\nClick to enable/disable";
                avglinetotal.Click += new EventHandler(PCCMS_Click);
                avglinetotal.Tag = pc;
                cms.Items.Add(avglinetotal);

                ToolStripMenuItem horlines = new ToolStripMenuItem(ccmshorizlines);
                horlines.ToolTipText = "Displays horizontal grid lines\r\nClick to enable/disable";
                horlines.Click += new EventHandler(PCCMS_Click);
                horlines.Tag = pc;
                cms.Items.Add(horlines);

                ToolStripMenuItem vertlines = new ToolStripMenuItem(ccmsvertlines);
                vertlines.ToolTipText = "Displays vertical grid lines\r\nClick to enable/disable";
                vertlines.Click += new EventHandler(PCCMS_Click);
                vertlines.Tag = pc;
                cms.Items.Add(vertlines);

                ToolStripMenuItem alarmsetpointlines = new ToolStripMenuItem(ccmsrenderalarmsetpoints);
                alarmsetpointlines.ToolTipText = "Master enable for rendering alarm setpoints";
                alarmsetpointlines.Click += new EventHandler(PCCMS_Click);
                alarmsetpointlines.Tag = pc;
                cms.Items.Add(alarmsetpointlines);

                ToolStripSeparator tss3 = new ToolStripSeparator();
                cms.Items.Add(tss3);

                ToolStripMenuItem updatetime = new ToolStripMenuItem(ccmsupdatetime);
                updatetime.ToolTipText = "Frequency the graph adds new values (in ms)\r\nClick to change";
                updatetime.Click += new EventHandler(PCCMS_Click);
                updatetime.Tag = pc;

                ToolStripMenuItem updatetimevalue = new ToolStripMenuItem(pc.TimerInterval.ToString());
                updatetimevalue.ToolTipText = "value is in ms";
                updatetimevalue.Tag = pc;
                updatetime.DropDownItems.Add(updatetimevalue);
                cms.Items.Add(updatetime);

                ToolStripMenuItem graphspacing = new ToolStripMenuItem(ccmsgraphspacing);
                graphspacing.ToolTipText = "spacing (in pixels) between each graph point";
                graphspacing.Click += new EventHandler(PCCMS_Click);
                graphspacing.Tag = pc;

                ToolStripMenuItem graphspacingvalue = new ToolStripMenuItem(pc.GridSpacing.ToString());
                graphspacingvalue.ToolTipText = "value is in pixels";
                graphspacingvalue.Tag = pc;
                graphspacing.DropDownItems.Add(graphspacingvalue);
                cms.Items.Add(graphspacing);

                ToolStripMenuItem maxentries = new ToolStripMenuItem(ccmsmaxentries);
                maxentries.ToolTipText = "max # of graph entries";
                maxentries.Click += new EventHandler(PCCMS_Click);
                maxentries.Tag = pc;

                ToolStripMenuItem maxentriesvalue = new ToolStripMenuItem(pc.MaxNumValues.ToString());
                maxentriesvalue.Tag = pc;
                maxentries.DropDownItems.Add(maxentriesvalue);
                cms.Items.Add(maxentries);

                ToolStripSeparator tss4 = new ToolStripSeparator();
                cms.Items.Add(tss4);

                ToolStripMenuItem addalarm = new ToolStripMenuItem(ccmsaddalarm);
                addalarm.ToolTipText = "adds a new alarm";
                addalarm.Click += new EventHandler(PCCMS_Click);
                addalarm.Tag = pc;
                cms.Items.Add(addalarm);

                ToolStripMenuItem deletealarm = new ToolStripMenuItem(ccmsdeletealarm);
                deletealarm.ToolTipText = "deletes an existing alarm";
                deletealarm.Click += new EventHandler(PCCMS_Click);
                deletealarm.Tag = pc;
                cms.Items.Add(deletealarm);

                ToolStripMenuItem viewalarms = new ToolStripMenuItem(ccmsviewalarms);
                viewalarms.ToolTipText = "view all the alarms defined";
                viewalarms.Click += new EventHandler(PCCMS_Click);
                viewalarms.Tag = pc;
                cms.Items.Add(viewalarms);

                ToolStripSeparator tss5 = new ToolStripSeparator();
                cms.Items.Add(tss5);

                ToolStripMenuItem stopgraph = new ToolStripMenuItem(ccmsstopgraph);
                stopgraph.ToolTipText = "stops the graph";
                stopgraph.Click += new EventHandler(PCCMS_Click);
                stopgraph.Tag = pc;
                cms.Items.Add(stopgraph);

                ToolStripMenuItem startgraph = new ToolStripMenuItem(ccmsstartgraph);
                startgraph.ToolTipText = "starts the graph";
                startgraph.Click += new EventHandler(PCCMS_Click);
                startgraph.Tag = pc;
                cms.Items.Add(startgraph);

                ToolStripMenuItem pausegraph = new ToolStripMenuItem(ccmspausegraph);
                pausegraph.ToolTipText = "pauses the graph. Values will still be recorded but graph will not be updated";
                pausegraph.Click += new EventHandler(PCCMS_Click);
                pausegraph.Tag = pc;
                cms.Items.Add(pausegraph);

                pc.ContextMenuStrip = cms;
            }
        }
        #endregion

        #region PCCMS_Click
        private void PCCMS_Click(object sender, EventArgs e)
        {
            ToolStripMenuItem tsmi = (ToolStripMenuItem)sender;
            SpPerfChart.PerfChart pc = (SpPerfChart.PerfChart)tsmi.Tag;
            String name = (String)pc.Tag;
            InputBox ib = null;
            DialogResult dr = DialogResult.Cancel;
            int index = perfcharts.IndexOf(pc);
            String tsmitext = tsmi.Text;

            try
            {
                if (tsmitext == ccmssetmax)
                {
                    ib = new InputBox(InputBox.inputtype.floatvalue, "Enter new max value for " + name);
                    dr = ib.ShowDialog();
                    if (dr != DialogResult.OK)
                    {
                        return;
                    }

                    SetNewPCMaxValue(pc, ib.FValue);
                }
                else if (tsmitext == ccmschangename)
                {
                    ib = new InputBox(InputBox.inputtype.strvalue, "Enter new name for " + name);
                    dr = ib.ShowDialog();
                    if (dr != DialogResult.OK)
                    {
                        return;
                    }

                    SetNewPCName(pc, ib.StrValue);
                }
                else if (tsmitext == ccmschartmodeabsolute || tsmitext == ccmschartmoderelative)
                {
                    SetNewPCChartMode(pc, tsmitext);
                }
                else if (tsmitext == ccmsavglineviewable)
                {
                    SetNewPCAvgLineViewable(pc);
                }
                else if (tsmitext == ccmsavglinetotal)
                {
                    SetNewPCAvgLineTotal(pc);
                }
                else if (tsmitext == ccmshorizlines)
                {
                    SetNewPCHorizLines(pc);
                }
                else if (tsmitext == ccmsvertlines)
                {
                    SetNewPCVertLines(pc);
                }
                else if (tsmitext == ccmsupdatetime)
                {
                    ib = new InputBox(InputBox.inputtype.intvalue, "Enter new update time interval for " + name);
                    dr = ib.ShowDialog();
                    if (dr != DialogResult.OK)
                    {
                        return;
                    }

                    SetNewPCTimerInterval(pc, Math.Abs(ib.IValue));
                }
                else if (tsmitext == ccmsgraphspacing)
                {
                    ib = new InputBox(InputBox.inputtype.intvalue, "Enter new graph spacing for " + name);
                    dr = ib.ShowDialog();
                    if (dr != DialogResult.OK)
                    {
                        return;
                    }

                    SetNewPCGraphSpacing(pc, Math.Abs(ib.IValue));
                }
                else if (tsmitext == ccmsmaxentries)
                {
                    ib = new InputBox(InputBox.inputtype.intvalue, "Enter new max # of graph entries for " + name);
                    dr = ib.ShowDialog();
                    if (dr != DialogResult.OK)
                    {
                        return;
                    }

                    SetNewPCMaxEntries(pc, Math.Abs(ib.IValue));
                }
                else if (tsmitext == ccmscleargraph)
                {
                    pc.Clear();
                }
                else if (tsmitext == ccmsstartgraph || tsmitext == ccmsstopgraph)
                {
                    CheckBox cb = pccbs[perfcharts.IndexOf(pc)];
                    cb.Checked = !cb.Checked;
                }
                else if (tsmitext == ccmspausegraph)
                {
                    pc.PerfChartStyle.Paused = !pc.PerfChartStyle.Paused;
                    tsmi.Checked = pc.PerfChartStyle.Paused;
                }
                else if (tsmitext == ccmsaddalarm)
                {
                    AddViewAlarm ava = null;

                    try
                    {
                        ava = new AddViewAlarm(null, name);
                        DialogResult drava = ava.ShowDialog();
                        if (drava != DialogResult.OK)
                        {
                            return;
                        }
                        SpPerfChart.PerfChart.AlarmSetpoint newalarm = new SpPerfChart.PerfChart.AlarmSetpoint(pc, ava.Setpoint, ava.Hystersis, ava.AlarmType, ava.Sound, ava.Message, ava.Rearm, ava.AlarmTime, ava.RenderSetpoint);
                        pc.AddAlarmSetPoint(newalarm);
                        pc.Invalidate();
                    }
                    finally
                    {
                        if (ava != null)
                        {
                            ava.Destroy();
                            ava.Dispose();
                            ava = null;
                        }
                    }
                }
                else if (tsmitext == ccmsdeletealarm || tsmitext == ccmsviewalarms)
                {
                    if (pc.AlarmSetpoints == null || pc.AlarmSetpoints.Count == 0)
                    {
                        MessageBox.Show(name + " has no alarms defined");
                    }
                    else
                    {
                        ViewAlarms va = null;

                        try
                        {
                            va = new ViewAlarms((tsmitext == ccmsviewalarms), pc);
                            if (va.ShowDialog() == DialogResult.OK)
                            {
                                pc.Invalidate();
                            }
                        }
                        finally
                        {
                            if (va != null)
                            {
                                va.Destroy();
                                va.Dispose();
                                va = null;
                            }
                        }
                    }
                }
                else if (tsmitext == ccmsrenderalarmsetpoints)
                {
                    tsmi.Checked = !tsmi.Checked;
                    pc.MasterRenderAlarmSetpointLines = tsmi.Checked;
                }
                else if (tsmitext == ccmstakepicture)
                {
                    SaveFileDialog sfd = null;
                    Bitmap b = null;

                    try
                    {
                        sfd = new SaveFileDialog();
                        sfd.AddExtension = true;
                        sfd.AutoUpgradeEnabled = false;
                        sfd.CheckFileExists = false;
                        sfd.CheckPathExists = true;
                        sfd.CreatePrompt = false;
                        sfd.DefaultExt = "bmp";
                        sfd.Filter = "BMP|*.bmp|JPEG|*.jpg|PNG|*.png|TGA|*.tga|TIF|*.tif|DDS|*.dds";
                        sfd.FilterIndex = 0;
                        sfd.InitialDirectory = picturesfolderpath;
                        sfd.OverwritePrompt = true;
                        sfd.RestoreDirectory = true;
                        sfd.ShowHelp = false;
                        sfd.SupportMultiDottedExtensions = false;
                        sfd.Title = "Save " + (String)pc.Tag + " to...";
                        sfd.ValidateNames = true;
                        DialogResult drb = sfd.ShowDialog();
                        if (drb != DialogResult.OK || sfd.FileName == null || sfd.FileName == "")
                        {
                            return;
                        }

                        b = new Bitmap(pc.Width, pc.Height);
                        pc.DrawToBitmap(b, new Rectangle(new Point(0, 0), new Size(pc.Width, pc.Height)));
                        b.Save(sfd.FileName);

                        MessageBox.Show("Saved successfully to " + sfd.FileName, "Take picture", MessageBoxButtons.OK);
                    }
                    finally
                    {
                        if (sfd != null)
                        {
                            sfd.Dispose();
                            sfd = null;
                        }

                        if (b != null)
                        {
                            b.Dispose();
                            b = null;
                        }
                    }
                }
            }
            finally
            {
                if (ib != null)
                {
                    ib.Dispose();
                    ib = null;
                }
            }
        }
        #endregion

        #region SetNewPCMaxValue
        private void SetNewPCMaxValue(SpPerfChart.PerfChart pc, float newvalue)
        {
            if (pc == null)
            {
                return;
            }

            float oldvalue = -1;

            if (pc == pcAirTemp)
            {
                oldvalue = max_air_temp;
                max_air_temp = newvalue;
                cfgfileentries[ccfgmaxairtemp][0] = newvalue.ToString(usculture);
            }
            else if (pc == pcBatteryVoltage)
            {
                oldvalue = max_battery_voltage;
                max_battery_voltage = newvalue;
                cfgfileentries[ccfgmaxbatteryvoltage][0] = newvalue.ToString(usculture);
            }
            else if (pc == pcIgnitionAdvance)
            {
                oldvalue = max_ignition_advance;
                max_ignition_advance = newvalue;
                cfgfileentries[ccfgmaxignitionadvance][0] = newvalue.ToString(usculture);
            }
            else if (pc == pcInjectorDuration)
            {
                oldvalue = max_injector_duration;
                max_injector_duration = newvalue;
                cfgfileentries[ccfgmaxinjectorduration][0] = newvalue.ToString(usculture);
            }
            else if (pc == pcMAP)
            {
                oldvalue = max_MAP;
                max_MAP = newvalue;
                cfgfileentries[ccfgmaxMAP][0] = newvalue.ToString(usculture);
            }
            else if (pc == pcRPM)
            {
                oldvalue = max_rpm;
                max_rpm = newvalue;
                cfgfileentries[ccfgmaxrpm][0] = newvalue.ToString(usculture);
            }
            else if (pc == pcThrottleAngle)
            {
                oldvalue = max_throttle_angle;
                max_throttle_angle = newvalue;
                cfgfileentries[ccfgmaxthrottleangle][0] = newvalue.ToString(usculture);
            }
            else if (pc == pcWaterTemp)
            {
                oldvalue = max_water_temp;
                max_water_temp = newvalue;
                cfgfileentries[ccfgmaxwatertemp][0] = newvalue.ToString(usculture);
            }
            else
            {
                return;
            }

            if (newvalue != oldvalue && this.Visible)
            {
                DisplayMessage((String)pc.Tag + " max changed to " + newvalue.ToString("F2"), true, true);
            }

            UpdatePCMaxValues();
        }
        #endregion

        #region SetNewPCName
        private void SetNewPCName(SpPerfChart.PerfChart pc, String newname)
        {
            if (pc == null)
            {
                return;
            }

            String oldname = (String)pc.Tag;
            pc.Tag = newname;

            if (pc == pcAirTemp)
            {
                cfgfileentries[ccfgairtempname][0] = newname;
            }
            else if (pc == pcBatteryVoltage)
            {
                cfgfileentries[ccfgbatteryvoltagename][0] = newname;
            }
            else if (pc == pcIgnitionAdvance)
            {
                cfgfileentries[ccfgignitionadvancename][0] = newname;
            }
            else if (pc == pcInjectorDuration)
            {
                cfgfileentries[ccfginjectordurationname][0] = newname;
            }
            else if (pc == pcMAP)
            {
                cfgfileentries[ccfgMAPname][0] = newname;
            }
            else if (pc == pcRPM)
            {
                cfgfileentries[ccfgRPMname][0] = newname;
            }
            else if (pc == pcThrottleAngle)
            {
                cfgfileentries[ccfgthrottleanglename][0] = newname;
            }
            else if (pc == pcWaterTemp)
            {
                cfgfileentries[ccfgwatertempname][0] = newname;
            }
            else
            {
                return;
            }

            if (oldname != newname && this.Visible)
            {
                DisplayMessage(oldname + " changed to " + newname, true, true);
            }

            UpdatePCMaxValues();
        }
        #endregion

        #region SetNewPCChartMode
        private void SetNewPCChartMode(SpPerfChart.PerfChart pc, String newchartmode)
        {
            if (pc == null)
            {
                return;
            }

            ToolStripMenuItem rel = null;
            ToolStripMenuItem abs = null;
            ToolStripMenuItem temp = null;

            foreach (object tsmi in pc.ContextMenuStrip.Items)
            {
                if (tsmi is ToolStripMenuItem)
                {
                    temp = (ToolStripMenuItem)tsmi;

                    if (temp.Text == ccmschartmode)
                    {
                        foreach (ToolStripMenuItem tsmi2 in temp.DropDownItems)
                        {
                            if (tsmi2.Text == ccmschartmodeabsolute)
                            {
                                abs = tsmi2;
                            }
                            else if (tsmi2.Text == ccmschartmoderelative)
                            {
                                rel = tsmi2;
                            }
                        }
                    }

                    if (abs != null && rel != null)
                    {
                        break;
                    }
                }
            }

            String oldchartmode = pc.ScaleMode.ToString();
            if (newchartmode == ccmschartmodeabsolute)
            {
                pc.ScaleMode = SpPerfChart.ScaleMode.Absolute;
                abs.Checked = true;
                rel.Checked = false;
            }
            else if (newchartmode == ccmschartmoderelative)
            {
                pc.ScaleMode = SpPerfChart.ScaleMode.Relative;
                abs.Checked = false;
                rel.Checked = true;
            }
            else
            {
                return;
            }

            String index = Convert.ToInt32(pc.ScaleMode).ToString();

            if (pc == pcAirTemp)
            {
                cfgfileentries[ccfgairtempchartmode][0] = index;
            }
            else if (pc == pcBatteryVoltage)
            {
                cfgfileentries[ccfgbatteryvoltagechartmode][0] = index;
            }
            else if (pc == pcIgnitionAdvance)
            {
                cfgfileentries[ccfgignitionadvancechartmode][0] = index;
            }
            else if (pc == pcInjectorDuration)
            {
                cfgfileentries[ccfginjectordurationchartmode][0] = index;
            }
            else if (pc == pcMAP)
            {
                cfgfileentries[ccfgMAPchartmode][0] = index;
            }
            else if (pc == pcRPM)
            {
                cfgfileentries[ccfgRPMchartmode][0] = index;
            }
            else if (pc == pcThrottleAngle)
            {
                cfgfileentries[ccfgthrottleanglechartmode][0] = index;
            }
            else if (pc == pcWaterTemp)
            {
                cfgfileentries[ccfgwatertempchartmode][0] = index;
            }
            else
            {
                return;
            }

            if (oldchartmode != pc.ScaleMode.ToString() && this.Visible)
            {
                DisplayMessage((String)pc.Tag + "'s chart mode changed to " + pc.ScaleMode.ToString(), true, true);
            }
        }
        #endregion

        #region SetNewPCAvgLineViewable
        private void SetNewPCAvgLineViewable(SpPerfChart.PerfChart pc)
        {
            if (pc == null)
            {
                return;
            }

            String oldvalue = pc.PerfChartStyle.ShowAverageLineViewable.ToString();
            pc.PerfChartStyle.ShowAverageLineViewable = !pc.PerfChartStyle.ShowAverageLineViewable;
            String newvalue = pc.PerfChartStyle.ShowAverageLineViewable.ToString();

            ToolStripMenuItem temp = null;
            foreach (object o in pc.ContextMenuStrip.Items)
            {
                if (o is ToolStripMenuItem)
                {
                    temp = (ToolStripMenuItem)o;

                    if (temp.Text == ccmsavglineviewable)
                    {
                        temp.Checked = pc.PerfChartStyle.ShowAverageLineViewable;
                        break;
                    }
                }
            }

            if (pc == pcAirTemp)
            {
                cfgfileentries[ccfgairtempavglineviewable][0] = newvalue;
            }
            else if (pc == pcBatteryVoltage)
            {
                cfgfileentries[ccfgbatteryvoltageavglineviewable][0] = newvalue;
            }
            else if (pc == pcIgnitionAdvance)
            {
                cfgfileentries[ccfgignitionadvanceavglineviewable][0] = newvalue;
            }
            else if (pc == pcInjectorDuration)
            {
                cfgfileentries[ccfginjectordurationavglineviewable][0] = newvalue;
            }
            else if (pc == pcMAP)
            {
                cfgfileentries[ccfgMAPavglineviewable][0] = newvalue;
            }
            else if (pc == pcRPM)
            {
                cfgfileentries[ccfgRPMavglineviewable][0] = newvalue;
            }
            else if (pc == pcThrottleAngle)
            {
                cfgfileentries[ccfgthrottleangleavglineviewable][0] = newvalue;
            }
            else if (pc == pcWaterTemp)
            {
                cfgfileentries[ccfgwatertempavglineviewable][0] = newvalue;
            }
            else
            {
                return;
            }

            if (oldvalue != newvalue && this.Visible)
            {
                DisplayMessage((String)pc.Tag + "'s show average line viewable changed to " + newvalue, true, true);
            }
        }
        #endregion

        #region SetNewPCAvgLineTotal
        private void SetNewPCAvgLineTotal(SpPerfChart.PerfChart pc)
        {
            if (pc == null)
            {
                return;
            }

            String oldvalue = pc.PerfChartStyle.ShowAverageLineTotal.ToString();
            pc.PerfChartStyle.ShowAverageLineTotal = !pc.PerfChartStyle.ShowAverageLineTotal;
            String newvalue = pc.PerfChartStyle.ShowAverageLineTotal.ToString();

            ToolStripMenuItem temp = null;
            foreach (object o in pc.ContextMenuStrip.Items)
            {
                if (o is ToolStripMenuItem)
                {
                    temp = (ToolStripMenuItem)o;

                    if (temp.Text == ccmsavglinetotal)
                    {
                        temp.Checked = pc.PerfChartStyle.ShowAverageLineTotal;
                        break;
                    }
                }
            }

            if (pc == pcAirTemp)
            {
                cfgfileentries[ccfgairtempavglinetotal][0] = newvalue;
            }
            else if (pc == pcBatteryVoltage)
            {
                cfgfileentries[ccfgbatteryvoltageavglinetotal][0] = newvalue;
            }
            else if (pc == pcIgnitionAdvance)
            {
                cfgfileentries[ccfgignitionadvanceavglinetotal][0] = newvalue;
            }
            else if (pc == pcInjectorDuration)
            {
                cfgfileentries[ccfginjectordurationavglinetotal][0] = newvalue;
            }
            else if (pc == pcMAP)
            {
                cfgfileentries[ccfgMAPavglinetotal][0] = newvalue;
            }
            else if (pc == pcRPM)
            {
                cfgfileentries[ccfgRPMavglinetotal][0] = newvalue;
            }
            else if (pc == pcThrottleAngle)
            {
                cfgfileentries[ccfgthrottleangleavglinetotal][0] = newvalue;
            }
            else if (pc == pcWaterTemp)
            {
                cfgfileentries[ccfgwatertempavglinetotal][0] = newvalue;
            }
            else
            {
                return;
            }

            if (oldvalue != newvalue && this.Visible)
            {
                DisplayMessage((String)pc.Tag + "'s show average line total changed to " + newvalue, true, true);
            }
        }
        #endregion

        #region SetNewPCHorizLines
        private void SetNewPCHorizLines(SpPerfChart.PerfChart pc)
        {
            if (pc == null)
            {
                return;
            }

            String oldvalue = pc.PerfChartStyle.ShowHorizontalGridLines.ToString();
            pc.PerfChartStyle.ShowHorizontalGridLines = !pc.PerfChartStyle.ShowHorizontalGridLines;
            String newvalue = pc.PerfChartStyle.ShowHorizontalGridLines.ToString();

            ToolStripMenuItem temp = null;
            foreach (object o in pc.ContextMenuStrip.Items)
            {
                if (o is ToolStripMenuItem)
                {
                    temp = (ToolStripMenuItem)o;

                    if (temp.Text == ccmshorizlines)
                    {
                        temp.Checked = pc.PerfChartStyle.ShowHorizontalGridLines;
                        break;
                    }
                }
            }

            if (pc == pcAirTemp)
            {
                cfgfileentries[ccfgairtemphorizlines][0] = newvalue;
            }
            else if (pc == pcBatteryVoltage)
            {
                cfgfileentries[ccfgbatteryvoltagehorizlines][0] = newvalue;
            }
            else if (pc == pcIgnitionAdvance)
            {
                cfgfileentries[ccfgignitionadvancehorizlines][0] = newvalue;
            }
            else if (pc == pcInjectorDuration)
            {
                cfgfileentries[ccfginjectordurationhorizlines][0] = newvalue;
            }
            else if (pc == pcMAP)
            {
                cfgfileentries[ccfgMAPhorizlines][0] = newvalue;
            }
            else if (pc == pcRPM)
            {
                cfgfileentries[ccfgRPMhorizlines][0] = newvalue;
            }
            else if (pc == pcThrottleAngle)
            {
                cfgfileentries[ccfgthrottleanglehorizlines][0] = newvalue;
            }
            else if (pc == pcWaterTemp)
            {
                cfgfileentries[ccfgwatertemphorizlines][0] = newvalue;
            }
            else
            {
                return;
            }

            if (newvalue != oldvalue && this.Visible)
            {
                DisplayMessage((String)pc.Tag + "'s show horizontal grid lines changed to " + newvalue, true, true);
            }
        }
        #endregion

        #region SetNewPCVertLines
        private void SetNewPCVertLines(SpPerfChart.PerfChart pc)
        {
            if (pc == null)
            {
                return;
            }

            String oldvalue = pc.PerfChartStyle.ShowVerticalGridLines.ToString();
            pc.PerfChartStyle.ShowVerticalGridLines = !pc.PerfChartStyle.ShowVerticalGridLines;
            String newvalue = pc.PerfChartStyle.ShowVerticalGridLines.ToString();

            ToolStripMenuItem temp = null;
            foreach (object o in pc.ContextMenuStrip.Items)
            {
                if (o is ToolStripMenuItem)
                {
                    temp = (ToolStripMenuItem)o;

                    if (temp.Text == ccmsvertlines)
                    {
                        temp.Checked = pc.PerfChartStyle.ShowVerticalGridLines;
                        break;
                    }
                }
            }

            if (pc == pcAirTemp)
            {
                cfgfileentries[ccfgairtempvertlines][0] = newvalue;
            }
            else if (pc == pcBatteryVoltage)
            {
                cfgfileentries[ccfgbatteryvoltagevertlines][0] = newvalue;
            }
            else if (pc == pcIgnitionAdvance)
            {
                cfgfileentries[ccfgignitionadvancevertlines][0] = newvalue;
            }
            else if (pc == pcInjectorDuration)
            {
                cfgfileentries[ccfginjectordurationvertlines][0] = newvalue;
            }
            else if (pc == pcMAP)
            {
                cfgfileentries[ccfgMAPvertlines][0] = newvalue;
            }
            else if (pc == pcRPM)
            {
                cfgfileentries[ccfgRPMvertlines][0] = newvalue;
            }
            else if (pc == pcThrottleAngle)
            {
                cfgfileentries[ccfgthrottleanglevertlines][0] = newvalue;
            }
            else if (pc == pcWaterTemp)
            {
                cfgfileentries[ccfgwatertempvertlines][0] = newvalue;
            }
            else
            {
                return;
            }

            if (newvalue != oldvalue && this.Visible)
            {
                DisplayMessage((String)pc.Tag + "'s show vertical grid lines changed to " + newvalue, true, true);
            }
        }
        #endregion

        #region SetNewPCTimerInterval
        private void SetNewPCTimerInterval(SpPerfChart.PerfChart pc, int newupdateinterval)
        {
            if (pc == null)
            {
                return;
            }

            ToolStripMenuItem temp = null;

            foreach (object tsmi in pc.ContextMenuStrip.Items)
            {
                if (tsmi is ToolStripMenuItem)
                {
                    temp = (ToolStripMenuItem)tsmi;

                    if (temp.Text == ccmsupdatetime)
                    {
                        temp.DropDownItems[0].Text = newupdateinterval.ToString();
                        break;
                    }
                }
            }

            String oldvalue = pc.TimerInterval.ToString();
            pc.TimerInterval = newupdateinterval;
            String newvalue = newupdateinterval.ToString();

            if (pc == pcAirTemp)
            {
                cfgfileentries[ccfgairtempupdatetime][0] = newvalue;
            }
            else if (pc == pcBatteryVoltage)
            {
                cfgfileentries[ccfgbatteryvoltageupdatetime][0] = newvalue;
            }
            else if (pc == pcIgnitionAdvance)
            {
                cfgfileentries[ccfgignitionadvanceupdatetime][0] = newvalue;
            }
            else if (pc == pcInjectorDuration)
            {
                cfgfileentries[ccfginjectordurationupdatetime][0] = newvalue;
            }
            else if (pc == pcMAP)
            {
                cfgfileentries[ccfgMAPupdatetime][0] = newvalue;
            }
            else if (pc == pcRPM)
            {
                cfgfileentries[ccfgRPMupdatetime][0] = newvalue;
            }
            else if (pc == pcThrottleAngle)
            {
                cfgfileentries[ccfgthrottleangleupdatetime][0] = newvalue;
            }
            else if (pc == pcWaterTemp)
            {
                cfgfileentries[ccfgwatertempupdatetime][0] = newvalue;
            }
            else
            {
                return;
            }

            if (newvalue != oldvalue && this.Visible)
            {
                DisplayMessage((String)pc.Tag + "'s update interval changed to " + newvalue, true, true);
            }
        }
        #endregion

        #region SetNewPCGraphSpacing
        private void SetNewPCGraphSpacing(SpPerfChart.PerfChart pc, int newgraphspacing)
        {
            if (pc == null)
            {
                return;
            }

            ToolStripMenuItem temp = null;

            foreach (object tsmi in pc.ContextMenuStrip.Items)
            {
                if (tsmi is ToolStripMenuItem)
                {
                    temp = (ToolStripMenuItem)tsmi;

                    if (temp.Text == ccmsgraphspacing)
                    {
                        temp.DropDownItems[0].Text = newgraphspacing.ToString();
                        break;
                    }
                }
            }

            String oldvalue = pc.GridSpacing.ToString();
            pc.GridSpacing = newgraphspacing; ;
            String newvalue = newgraphspacing.ToString();

            if (pc == pcAirTemp)
            {
                cfgfileentries[ccfgairtempgraphspacing][0] = newvalue;
            }
            else if (pc == pcBatteryVoltage)
            {
                cfgfileentries[ccfgbatteryvoltagegraphspacing][0] = newvalue;
            }
            else if (pc == pcIgnitionAdvance)
            {
                cfgfileentries[ccfgignitionadvancegraphspacing][0] = newvalue;
            }
            else if (pc == pcInjectorDuration)
            {
                cfgfileentries[ccfginjectordurationgraphspacing][0] = newvalue;
            }
            else if (pc == pcMAP)
            {
                cfgfileentries[ccfgMAPgraphspacing][0] = newvalue;
            }
            else if (pc == pcRPM)
            {
                cfgfileentries[ccfgRPMgraphspacing][0] = newvalue;
            }
            else if (pc == pcThrottleAngle)
            {
                cfgfileentries[ccfgthrottleanglegraphspacing][0] = newvalue;
            }
            else if (pc == pcWaterTemp)
            {
                cfgfileentries[ccfgwatertempgraphspacing][0] = newvalue;
            }
            else
            {
                return;
            }

            if (newvalue != oldvalue && this.Visible)
            {
                DisplayMessage((String)pc.Tag + "'s graph spacing changed to " + newvalue, true, true);
            }
        }
        #endregion

        #region SetNewPCMaxEntries
        private void SetNewPCMaxEntries(SpPerfChart.PerfChart pc, int newmaxentries)
        {
            if (pc == null)
            {
                return;
            }

            ToolStripMenuItem temp = null;

            foreach (object tsmi in pc.ContextMenuStrip.Items)
            {
                if (tsmi is ToolStripMenuItem)
                {
                    temp = (ToolStripMenuItem)tsmi;

                    if (temp.Text == ccmsmaxentries)
                    {
                        temp.DropDownItems[0].Text = newmaxentries.ToString();
                        break;
                    }
                }
            }

            String oldvalue = pc.MaxNumValues.ToString();
            pc.MaxNumValues = newmaxentries;
            String newvalue = newmaxentries.ToString();

            if (pc == pcAirTemp)
            {
                cfgfileentries[ccfgairtempmaxentries][0] = newvalue;
            }
            else if (pc == pcBatteryVoltage)
            {
                cfgfileentries[ccfgbatteryvoltagemaxentries][0] = newvalue;
            }
            else if (pc == pcIgnitionAdvance)
            {
                cfgfileentries[ccfgignitionadvancemaxentries][0] = newvalue;
            }
            else if (pc == pcInjectorDuration)
            {
                cfgfileentries[ccfginjectordurationmaxentries][0] = newvalue;
            }
            else if (pc == pcMAP)
            {
                cfgfileentries[ccfgMAPmaxentries][0] = newvalue;
            }
            else if (pc == pcRPM)
            {
                cfgfileentries[ccfgRPMmaxentries][0] = newvalue;
            }
            else if (pc == pcThrottleAngle)
            {
                cfgfileentries[ccfgthrottleanglemaxentries][0] = newvalue;
            }
            else if (pc == pcWaterTemp)
            {
                cfgfileentries[ccfgwatertempmaxentries][0] = newvalue;
            }
            else
            {
                return;
            }

            if (newvalue != oldvalue && this.Visible)
            {
                DisplayMessage((String)pc.Tag + "'s max # of graph entries changed to " + newvalue, true, true);
            }
        }
        #endregion

        #region SetNewPCTimerMode
        private void SetNewPCTimerMode(SpPerfChart.PerfChart pc, String newvalue)
        {
            if (pc == null)
            {
                return;
            }

            String oldvalue = pc.TimerMode.ToString();
            string[] names = Enum.GetNames(typeof(SpPerfChart.TimerMode));
            for (int i = 0; i < names.Length; i++)
            {
                if (names[i] == newvalue)
                {
                    pc.TimerMode = (SpPerfChart.TimerMode)(i);
                    break;
                }
            }

            if (pc == pcAirTemp)
            {
                cfgfileentries[ccfgairtemptimermode][0] = newvalue;
            }
            else if (pc == pcBatteryVoltage)
            {
                cfgfileentries[ccfgbatteryvoltagetimermode][0] = newvalue;
            }
            else if (pc == pcIgnitionAdvance)
            {
                cfgfileentries[ccfgignitionadvancetimermode][0] = newvalue;
            }
            else if (pc == pcInjectorDuration)
            {
                cfgfileentries[ccfginjectordurationtimermode][0] = newvalue;
            }
            else if (pc == pcMAP)
            {
                cfgfileentries[ccfgMAPtimermode][0] = newvalue;
            }
            else if (pc == pcRPM)
            {
                cfgfileentries[ccfgRPMtimermode][0] = newvalue;
            }
            else if (pc == pcThrottleAngle)
            {
                cfgfileentries[ccfgthrottleangletimermode][0] = newvalue;
            }
            else if (pc == pcWaterTemp)
            {
                cfgfileentries[ccfgwatertemptimermode][0] = newvalue;
            }
            else
            {
                return;
            }

            if (newvalue != oldvalue && this.Visible)
            {
                DisplayMessage((String)pc.Tag + "'s timer mode changed to " + newvalue, true, true);
            }
        }

        private void SetNewPCTimerMode(SpPerfChart.PerfChart pc, bool updatecfg)
        {
            if (pc == null)
            {
                return;
            }

            CheckBox s = pccbs[perfcharts.IndexOf(pc)];
            String strchecked = s.Checked.ToString();
            SpPerfChart.TimerMode tm = cdefaulttimermode;
            if (!master_poll_enable || serial_comms.ECU_Initialized != DialogResult.Yes || !s.Checked)
            {
                tm = SpPerfChart.TimerMode.Disabled;
            }

            bool started = false;

            if (s == cbAirTemp)
            {
                pcAirTemp.TimerMode = tm;
                if (updatecfg)
                {
                    cfgfileentries[ccfgairtemploggingenabled][0] = strchecked;
                }
                started = Convert.ToBoolean(cfgfileentries[ccfgairtemploggingenabled][0]);
            }
            else if (s == cbBatteryVoltage)
            {
                pcBatteryVoltage.TimerMode = tm;
                if (updatecfg)
                {
                    cfgfileentries[ccfgbatteryvoltageloggingenabled][0] = strchecked;
                }
                started = Convert.ToBoolean(cfgfileentries[ccfgbatteryvoltageloggingenabled][0]);
            }
            else if (s == cbIgnitionAdvance)
            {
                pcIgnitionAdvance.TimerMode = tm;
                if (updatecfg)
                {
                    cfgfileentries[ccfgignitionadvanceloggingenabled][0] = strchecked;
                }
                started = Convert.ToBoolean(cfgfileentries[ccfgignitionadvanceloggingenabled][0]);
            }
            else if (s == cbInjectorDuration)
            {
                pcInjectorDuration.TimerMode = tm;
                if (updatecfg)
                {
                    cfgfileentries[ccfginjectordurationloggingenabled][0] = strchecked;
                }
                started = Convert.ToBoolean(cfgfileentries[ccfginjectordurationloggingenabled][0]);
            }
            else if (s == cbMAP)
            {
                pcMAP.TimerMode = tm;
                if (updatecfg)
                {
                    cfgfileentries[ccfgMAPloggingenabled][0] = strchecked;
                }
                started = Convert.ToBoolean(cfgfileentries[ccfgMAPloggingenabled][0]);
            }
            else if (s == cbRPM)
            {
                pcRPM.TimerMode = tm;
                if (updatecfg)
                {
                    cfgfileentries[ccfgRPMloggingenabled][0] = strchecked;
                }
                started = Convert.ToBoolean(cfgfileentries[ccfgMAPloggingenabled][0]);
            }
            else if (s == cbThrottleAngle)
            {
                pcThrottleAngle.TimerMode = tm;
                if (updatecfg)
                {
                    cfgfileentries[ccfgthrottleangleloggingenabled][0] = strchecked;
                }
                started = Convert.ToBoolean(cfgfileentries[ccfgthrottleangleloggingenabled][0]);
            }
            else if (s == cbWaterTemp)
            {
                pcWaterTemp.TimerMode = tm;
                if (updatecfg)
                {
                    cfgfileentries[ccfgwatertemploggingenabled][0] = strchecked;
                }
                started = Convert.ToBoolean(cfgfileentries[ccfgwatertemploggingenabled][0]);
            }

            ToolStripMenuItem temp = null;
            foreach (object tsmi in pc.ContextMenuStrip.Items)
            {
                if (tsmi is ToolStripMenuItem)
                {
                    temp = (ToolStripMenuItem)tsmi;

                    if (temp.Text == ccmsstopgraph)
                    {
                        temp.Checked = !started;
                    }
                    else if (temp.Text == ccmsstartgraph)
                    {
                        temp.Checked = started;
                    }
                }
            }
        }
        #endregion

        #region Execute Diagnostic test
        private void Execute_Diagnostic_Test(object sender, EventArgs e)
        {
            CheckBox cb = (CheckBox)sender;
            if (cb == null)
            {
                return;
            }

            byte dt = DTcodes[DTcbs.IndexOf(cb)];

            if (cb.Checked)
            {
                ShowDiagnosticTestStartMessage(dt);

                diagnostic_test_result = 0;
                SetDiagnosticTestInProgressStatus(true, ref cb);

                // ensure no characters in receive buffer
                DiscardInBuffer();

                // transmit $AA and diagnostic test wanted
                if (!Transmit(new byte[] { cperform_diagnostic_test, dt }, cdt_transmit_delay))
                {
                    DiscardInBuffer();
                    SetDiagnosticTestInProgressStatus(false, ref cb);
                    MessageBox.Show("Failed to initiate " + cb.Text + " diagnostic test", "Error", MessageBoxButtons.OK);
                    return;
                }

                bytes_received = Receive(2, cPID_receive_timeout);
                if (bytes_received != null)
                {
                    if (bytes_received[0] == cperform_diagnostic_test && bytes_received[1] == cperform_diagnostic_test)
                    {
                        bytes_received = Receive(2, cPID_receive_timeout);
                        if (bytes_received == null)
                        {
                            DiscardInBuffer();
                            SetDiagnosticTestInProgressStatus(false, ref cb);

                            MessageBox.Show("Failed to initiate " + cb.Text + " diagnostic test", "Error", MessageBoxButtons.OK);
                            cb.Checked = false;
                            return;
                        }
                        else
                        {
                            if (bytes_received[0] != dt && bytes_received[1] != dt)
                            {
                                DiscardInBuffer();
                                SetDiagnosticTestInProgressStatus(false, ref cb);

                                MessageBox.Show("Failed to initiate " + cb.Text + " diagnostic test", "Error", MessageBoxButtons.OK);
                                cb.Checked = false;
                                return;
                            }
                        }
                    }
                    else if (bytes_received[0] == cperform_diagnostic_test && bytes_received[1] == dt)
                    {
                        // do nothing
                    }
                }

                DiscardInBuffer();

                EnableDisableDTTerminationListener(true);
            }
            else
            {
                if (diagnostic_test_in_progress == null)
                {
                    return;
                }

                DiscardOutBuffer();
                DiscardInBuffer();

                if (!Transmit(cterminate_diagnostic_test, false))
                {
                    MessageBox.Show("Failed to deactivate " + cb.Text + " diagnostic test", "Error", MessageBoxButtons.OK);
                    return;
                }

                // let diagnostic test listener react
                while (dt_termination_listener != null && dt_termination_listener.IsAlive)
                {
                    Application.DoEvents();
                }

                // below will be handled by diagnostic test listener thread
                /*
                bytes_received = Receive(1, cPID_receive_timeout);
                if (bytes_received == null || bytes_received[0] != cterminate_diagnostic_test)
                {
                    MessageBox.Show("Failed to deactivate " + cb.Text + " diagnostic test", "Error", MessageBoxButtons.OK);
                    return;
                }
                */

                SetDiagnosticTestInProgressStatus(false, ref cb);

                if (diagnostic_test_result == cterminate_diagnostic_test || diagnostic_test_result == cdiagnostictest_finished_normally)
                {
                    if (diagnostic_test_result == cterminate_diagnostic_test)
                    {
                        MessageBox.Show("Diagnostic test was terminated by user or ECU", "Diagnostic testing", MessageBoxButtons.OK);
                    }
                    else
                    {
                        MessageBox.Show("Diagnostic test finished normally", "Diagnostic testing", MessageBoxButtons.OK);
                    }
                }

                // if user asked to clear EEPROM codes then show them message about how they are cleared
                if (dt == 0x84)
                {
                    MessageBox.Show("You must cycle the ignition switch on/off (and wait for ECU relay to drop out) before EEPROM errors are cleared", "Clear errors", MessageBoxButtons.OK);
                }

                if (clbAvailable_Comm_Ports.SelectedIndex > -1)
                {
                    DisplayMessage("Closing comm port", true, true);
                    clbAvailable_Comm_Ports.SetItemChecked(clbAvailable_Comm_Ports.SelectedIndex, false);
                }
            }
        }
        #endregion

        #region Show diagnostic test start message
        private void ShowDiagnosticTestStartMessage(byte dt)
        {
            String mes = "";

            switch (dt)
            {
                case 0x80:
                    mes = "This diagnostic test will operate the fuel pump for 30 seconds";
                    break;

                case 0x81:
                    mes = "This diagnostic test will operate the horizontal injector for 4ms of every second. The diagnostic test will run for 5 seconds";
                    break;

                case 0x82:
                    mes = "This diagnostic test will operate coil 1 for 2ms of every second. The diagnostic test will run for 5 seconds";
                    break;

                case 0x83:
                    mes = "This diagnostic test will operate coil 2 for 2ms of every second. The diagnostic test will run for 5 seconds";
                    break;

                case 0x86:
                    mes = "This diagnostic test will operate the tach at 125 Hz (3750rpm) for 2 seconds";
                    break;

                case 0x87:
                    mes = "This diagnostic test will operate the vertical injector for 4ms of every second. The diagnostic test will run for 5 seconds";
                    break;

                default:
                    mes = "";
                    break;
            }

            if (mes != "")
            {
                MessageBox.Show(mes, "Diagnostic test", MessageBoxButtons.OK);
            }
        }
        #endregion

        #region SetDiagnosticTestInProgressStatus
        private void SetDiagnosticTestInProgressStatus(bool DTstarted, ref CheckBox cbpassed)
        {
            /*
            try
            {
                SetPCsEnabledDisabled(!DTstarted);
            }
            catch
            {
                // do nothing
            }
            */
            
            master_poll_enable = !DTstarted && cbMasterPollingEnabled.Checked;

            try
            {
                SetDiagnosticCBsEnabledDisabled(!DTstarted, ref cbpassed, !DTstarted);
            }
            catch
            {
                // do nothing
            }

            if (DTstarted)
            {
                diagnostic_test_in_progress = cbpassed;
                diagnostic_test_result = 0;

                if (update_values != null && update_values.IsAlive)
                {
                    try
                    {
                        update_values.Suspend();

                        while ((update_values.ThreadState & System.Threading.ThreadState.Suspended) == 0)
                        {
                            Application.DoEvents();
                        }
                    }
                    catch
                    {
                        // do nothing
                    }
                }

                if (poll_ecu_data != null && poll_ecu_data.IsAlive)
                {
                    try
                    {
                        poll_ecu_data.Suspend();

                        while ((poll_ecu_data.ThreadState & System.Threading.ThreadState.Suspended) == 0)
                        {
                            Application.DoEvents();
                        }
                    }
                    catch
                    {
                        // do nothing
                    }
                }
            }
            else
            {
                EnableDisableDTTerminationListener(false);
                CheckBox cbdt = diagnostic_test_in_progress;
                diagnostic_test_in_progress = null;
                if (cbdt != null)
                {
                    cbdt.Checked = false;
                }
                cbdt = null;

                if (poll_ecu_data != null && poll_ecu_data.IsAlive && ((poll_ecu_data.ThreadState & System.Threading.ThreadState.Suspended) != 0))
                {
                    try
                    {
                        poll_ecu_data.Resume();
                    }
                    catch
                    {
                        // do nothing
                    }
                }

                if (update_values != null && update_values.IsAlive && ((update_values.ThreadState & System.Threading.ThreadState.Suspended) != 0))
                {
                    try
                    {
                        update_values.Resume();
                    }
                    catch
                    {
                        // do nothing
                    }
                }
            }
        }
        #endregion

        #region SetDiagnosticCBsEnabledDisabled
        private void SetDiagnosticCBsEnabledDisabled(bool enabled, ref CheckBox cbpassed, bool uncheck)
        {
            foreach (CheckBox cb in DTcbs)
            {
                if (cb == null)
                {
                    continue;
                }

                if (cb == cbpassed)
                {
                    if (uncheck)
                    {
                        cb.Checked = false;
                    }

                    continue;
                }

                if (uncheck)
                {
                    cb.Checked = false;
                }
                cb.Enabled = enabled;
            }
        }
        #endregion

        #region EnableDisableDTTerminationListener
        private void EnableDisableDTTerminationListener(bool enabled)
        {
            if (enabled)
            {
                dt_termination_listener = new Thread(new ThreadStart(ListenForDTTermination));
                dt_termination_listener.IsBackground = true;
                dt_termination_listener.Start();
            }
            else
            {
                if (dt_termination_listener != null)
                {
                    if (dt_termination_listener.IsAlive)
                    {
                        dt_termination_listener.Abort();
                        while (dt_termination_listener.IsAlive)
                        {
                            Application.DoEvents();
                        }
                    }
                    dt_termination_listener = null;
                }
            }
        }
        #endregion

        #region ListenForDTTermination
        private void ListenForDTTermination()
        {
            List<Byte> bytes_received = null;

            while (!form_closing && diagnostic_test_in_progress != null)
            {
                bytes_received = Receive(1, 0);
                if (bytes_received != null)
                {
                    if (bytes_received[0] == cterminate_diagnostic_test)
                    {
                        diagnostic_test_result = cterminate_diagnostic_test;
                        DiscardInBuffer();
                        //SetDiagnosticTestInProgressStatus(false, ref diagnostic_test_in_progress);
                        break;
                    }
                    else if (bytes_received[0] == cdiagnostictest_finished_normally)
                    {
                        diagnostic_test_result = cdiagnostictest_finished_normally;
                        DiscardInBuffer();
                        //SetDiagnosticTestInProgressStatus(false, ref diagnostic_test_in_progress);
                        break;
                    }
                    else
                    {
                    }
                }
            }
        }
        #endregion

        #region cbMasterPollingEnabled_CheckedChanged
        private void cbMasterPollingEnabled_CheckedChanged(object sender, EventArgs e)
        {
            if (cbMasterPollingEnabled.Checked)
            {
                if (diagnostic_test_in_progress == null)
                {
                    master_poll_enable = true;
                    cbMasterPollingEnabled.Text = "Polling enabled";
                    //SetPCsEnabledDisabled(true);
                }
                else
                {
                    cbMasterPollingEnabled.Checked = false;
                    //master_poll_enable = false;
                    //cbMasterPollingEnabled.Checked = false;
                    //SetPCsEnabledDisabled(false);
                }
            }
            else
            {
                master_poll_enable = false;
                cbMasterPollingEnabled.Text = "Polling disabled";
                //SetPCsEnabledDisabled(false);
            }

            cfgfileentries[ccfgmasterpollenabled][0] = master_poll_enable.ToString();
        }
        #endregion

        #region SplitterDistanceChanged
        private void SplitterDistanceChanged(object sender, SplitterEventArgs e)
        {
            if (!loaded)
            {
                return;
            }

            SplitContainer sc = (SplitContainer)sender;
            String splitterdistance = sc.SplitterDistance.ToString();

            try
            {
                if (sc == scGraphsBottom)
                {
                    cfgfileentries[ccfgsplitterbottom][0] = splitterdistance;
                }
                else if (sc == scGraphsBottomLeft)
                {
                    cfgfileentries[ccfgsplitterbottomleft][0] = splitterdistance;
                }
                else if (sc == scGraphsBottomRight)
                {
                    cfgfileentries[ccfgsplitterbottomright][0] = splitterdistance;
                }
                else if (sc == scGraphsHorizontal)
                {
                    cfgfileentries[ccfgsplitterhorizontal][0] = splitterdistance;
                }
                else if (sc == scGraphsTop)
                {
                    cfgfileentries[ccfgsplittertop][0] = splitterdistance;
                }
                else if (sc == scGraphsTopLeft)
                {
                    cfgfileentries[ccfgsplittertopleft][0] = splitterdistance;
                }
                else if (sc == scGraphsTopRight)
                {
                    cfgfileentries[ccfgsplittertopright][0] = splitterdistance;
                }
            }
            catch
            {
                // do nothing - constructor will cause this to be called during initialization
            }
        }
        #endregion

        // for config file
        #region Read cfg file
        private void ReadCfgFile()
        {
            cfgfileentries.Clear();

            // determine cfg file path
            String[] pathsplit = Application.ExecutablePath.Split('\\');
            String cfgfilename = pathsplit[pathsplit.Length - 1];
            cfgfilename = cfgfilename.Remove(cfgfilename.LastIndexOf('.'));
            cfgfilename += ".cfg";
            cfgfilepath = Application.StartupPath + "\\" + cfgfilename;
            bool exists = File.Exists(cfgfilepath);
            Trace.WriteLine(cfgfilepath + " exists=" + exists);
            List<String> entries = new List<string>();

            // if exists then read in items to look for
            if (exists)
            {
                Trace.WriteLine("Reading in cfg file...");

                FileStream fs = null;
                StreamReader sr = null;

                try
                {
                    fs = File.Open(cfgfilepath, FileMode.Open, FileAccess.Read);
                }
                catch
                {
                    fs = null;
                    Trace.WriteLine("Unable to open " + cfgfilepath + " for reading");
                    return;
                }

                // read in the entire file at once
                sr = new StreamReader(fs);
                //entries = sr.ReadToEnd().Split(new char[] { '\r' }).ToList();
                String[] strentries = sr.ReadToEnd().Split(new char[] { '\r' });
                foreach (String s in strentries)
                {
                    entries.Add(s);
                }
                sr.Close();
                fs.Dispose();
                fs = null;

                String headername = "";

                // parse file
                for (int i = 0; i < entries.Count; i++)
                {
                    Application.DoEvents();
                    entries[i] = entries[i].Trim();
                    if (entries[i] == "" || entries[i].StartsWith(";"))
                    {
                        entries.RemoveAt(i);
                        i--;
                        continue;
                    }

                    if (entries[i].StartsWith("[") && entries[i].EndsWith("]"))
                    {
                        // end of header
                        if (headername != "")
                        {
                            headername = "";
                            continue;
                        }

                        // start of header
                        headername = entries[i];
                        if (!cfgfileentries.ContainsKey(headername))
                        {
                            cfgfileentries[headername] = new List<string>();
                            Trace.WriteLine("Header found: " + headername);
                        }
                        else
                        {
                            cfgfileentries[headername].Clear();
                        }
                    }
                    else if (headername != "")
                    {
                        // add header entries
                        cfgfileentries[headername].Add(entries[i]);
                    }
                    else if (entries[i].Contains("="))
                    {
                        String[] linesplit = new String[2];
                        linesplit[0] = entries[i].Substring(0, entries[i].IndexOf('=')).Trim();
                        linesplit[1] = entries[i].Substring(entries[i].IndexOf('=') + 1).Trim();
                        // create dictionary entry
                        if (!cfgfileentries.ContainsKey(linesplit[0]))
                        {
                            linesplit[1] = linesplit[1].Replace("|", "\r\n");
                            cfgfileentries[linesplit[0]] = new List<string>();
                            cfgfileentries[linesplit[0]].Add(linesplit[1]);
                            Trace.WriteLine("Entry found: " + entries[i]);
                        }
                        else
                        {
                            cfgfileentries[linesplit[0]].Add(linesplit[1]);
                        }
                    }
                }
            }

            if (!cfgfileentries.ContainsKey(cdebugautomaticallystart))
            {
                cfgfileentries[cdebugautomaticallystart] = new List<string>() { false.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgsplitterhorizontal))
            {
                cfgfileentries[ccfgsplitterhorizontal] = new List<string>() { chorizontalsplitterdistance.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgsplittertopleft))
            {
                cfgfileentries[ccfgsplittertopleft] = new List<string>() { ctopleftsplitterdistance.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgsplittertopright))
            {
                cfgfileentries[ccfgsplittertopright] = new List<string>() { ctoprightsplitterdistance.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgsplittertop))
            {
                cfgfileentries[ccfgsplittertop] = new List<string>() { ctopsplitterdistance.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgsplitterbottomleft))
            {
                cfgfileentries[ccfgsplitterbottomleft] = new List<string>() { cbottomleftsplitterdistance.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgsplitterbottomright))
            {
                cfgfileentries[ccfgsplitterbottomright] = new List<string>() { cbottomrightsplitterdistance.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgsplitterbottom))
            {
                cfgfileentries[ccfgsplitterbottom] = new List<string>() { cbottomsplitterdistance.ToString() };
            }


            if (!cfgfileentries.ContainsKey(ccfgbatteryvoltagename))
            {
                cfgfileentries[ccfgbatteryvoltagename] = new List<string>() { (String)pcBatteryVoltage.Tag };
            }
            if (!cfgfileentries.ContainsKey(ccfgmaxbatteryvoltage))
            {
                cfgfileentries[ccfgmaxbatteryvoltage] = new List<string>() { max_battery_voltage.ToString(usculture) };
            }
            if (!cfgfileentries.ContainsKey(ccfgbatteryvoltagechartmode))
            {
                cfgfileentries[ccfgbatteryvoltagechartmode] = new List<string>() { Convert.ToInt32(pcBatteryVoltage.ScaleMode).ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgbatteryvoltageavglineviewable))
            {
                cfgfileentries[ccfgbatteryvoltageavglineviewable] = new List<string>() { pcBatteryVoltage.PerfChartStyle.ShowAverageLineViewable.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgbatteryvoltageavglinetotal))
            {
                cfgfileentries[ccfgbatteryvoltageavglinetotal] = new List<string>() { pcBatteryVoltage.PerfChartStyle.ShowAverageLineTotal.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgbatteryvoltagehorizlines))
            {
                cfgfileentries[ccfgbatteryvoltagehorizlines] = new List<string>() { pcBatteryVoltage.PerfChartStyle.ShowHorizontalGridLines.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgbatteryvoltagevertlines))
            {
                cfgfileentries[ccfgbatteryvoltagevertlines] = new List<string>() { pcBatteryVoltage.PerfChartStyle.ShowVerticalGridLines.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgbatteryvoltageupdatetime))
            {
                cfgfileentries[ccfgbatteryvoltageupdatetime] = new List<string>() { cstarting_poll_speed.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgbatteryvoltagetimermode))
            {
                cfgfileentries[ccfgbatteryvoltagetimermode] = new List<string>() { Convert.ToInt32(cdefaulttimermode).ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgbatteryvoltagegraphspacing))
            {
                cfgfileentries[ccfgbatteryvoltagegraphspacing] = new List<string>() { cdefault_graph_spacing.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgbatteryvoltagemaxentries))
            {
                cfgfileentries[ccfgbatteryvoltagemaxentries] = new List<string>() { cdefault_max_graph_entries.ToString() };
            }


            if (!cfgfileentries.ContainsKey(ccfgairtempname))
            {
                cfgfileentries[ccfgairtempname] = new List<string>() { (String)pcAirTemp.Tag };
            }
            if (!cfgfileentries.ContainsKey(ccfgmaxairtemp))
            {
                cfgfileentries[ccfgmaxairtemp] = new List<string>() { max_air_temp.ToString(usculture) };
            }
            if (!cfgfileentries.ContainsKey(ccfgairtempchartmode))
            {
                cfgfileentries[ccfgairtempchartmode] = new List<string>() { Convert.ToInt32(pcAirTemp.ScaleMode).ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgairtempavglineviewable))
            {
                cfgfileentries[ccfgairtempavglineviewable] = new List<string>() { pcAirTemp.PerfChartStyle.ShowAverageLineViewable.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgairtempavglinetotal))
            {
                cfgfileentries[ccfgairtempavglinetotal] = new List<string>() { pcAirTemp.PerfChartStyle.ShowAverageLineTotal.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgairtemphorizlines))
            {
                cfgfileentries[ccfgairtemphorizlines] = new List<string>() { pcAirTemp.PerfChartStyle.ShowHorizontalGridLines.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgairtempvertlines))
            {
                cfgfileentries[ccfgairtempvertlines] = new List<string>() { pcAirTemp.PerfChartStyle.ShowVerticalGridLines.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgairtempupdatetime))
            {
                cfgfileentries[ccfgairtempupdatetime] = new List<string>() { cstarting_poll_speed.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgairtemptimermode))
            {
                cfgfileentries[ccfgairtemptimermode] = new List<string>() { Convert.ToInt32(cdefaulttimermode).ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgairtempgraphspacing))
            {
                cfgfileentries[ccfgairtempgraphspacing] = new List<string>() { cdefault_graph_spacing.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgairtempmaxentries))
            {
                cfgfileentries[ccfgairtempmaxentries] = new List<string>() { cdefault_max_graph_entries.ToString() };
            }

            
            if (!cfgfileentries.ContainsKey(ccfgwatertempname))
            {
                cfgfileentries[ccfgwatertempname] = new List<string>() { (String)pcWaterTemp.Tag };
            }
            if (!cfgfileentries.ContainsKey(ccfgmaxwatertemp))
            {
                cfgfileentries[ccfgmaxwatertemp] = new List<string>() { max_water_temp.ToString(usculture) };
            }
            if (!cfgfileentries.ContainsKey(ccfgwatertempchartmode))
            {
                cfgfileentries[ccfgwatertempchartmode] = new List<string>() { Convert.ToInt32(pcWaterTemp.ScaleMode).ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgwatertempavglineviewable))
            {
                cfgfileentries[ccfgwatertempavglineviewable] = new List<string>() { pcWaterTemp.PerfChartStyle.ShowAverageLineViewable.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgwatertempavglinetotal))
            {
                cfgfileentries[ccfgwatertempavglinetotal] = new List<string>() { pcWaterTemp.PerfChartStyle.ShowAverageLineTotal.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgwatertemphorizlines))
            {
                cfgfileentries[ccfgwatertemphorizlines] = new List<string>() { pcWaterTemp.PerfChartStyle.ShowHorizontalGridLines.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgwatertempvertlines))
            {
                cfgfileentries[ccfgwatertempvertlines] = new List<string>() { pcWaterTemp.PerfChartStyle.ShowVerticalGridLines.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgwatertempupdatetime))
            {
                cfgfileentries[ccfgwatertempupdatetime] = new List<string>() { cstarting_poll_speed.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgwatertemptimermode))
            {
                cfgfileentries[ccfgwatertemptimermode] = new List<string>() { Convert.ToInt32(cdefaulttimermode).ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgwatertempgraphspacing))
            {
                cfgfileentries[ccfgwatertempgraphspacing] = new List<string>() { cdefault_graph_spacing.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgwatertempmaxentries))
            {
                cfgfileentries[ccfgwatertempmaxentries] = new List<string>() { cdefault_max_graph_entries.ToString() };
            }

            
            if (!cfgfileentries.ContainsKey(ccfgMAPname))
            {
                cfgfileentries[ccfgMAPname] = new List<string>() { (String)pcMAP.Tag };
            }
            if (!cfgfileentries.ContainsKey(ccfgmaxMAP))
            {
                cfgfileentries[ccfgmaxMAP] = new List<string>() { max_MAP.ToString(usculture) };
            }
            if (!cfgfileentries.ContainsKey(ccfgMAPchartmode))
            {
                cfgfileentries[ccfgMAPchartmode] = new List<string>() { Convert.ToInt32(pcMAP.ScaleMode).ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgMAPavglineviewable))
            {
                cfgfileentries[ccfgMAPavglineviewable] = new List<string>() { pcMAP.PerfChartStyle.ShowAverageLineViewable.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgMAPavglinetotal))
            {
                cfgfileentries[ccfgMAPavglinetotal] = new List<string>() { pcMAP.PerfChartStyle.ShowAverageLineTotal.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgMAPhorizlines))
            {
                cfgfileentries[ccfgMAPhorizlines] = new List<string>() { pcMAP.PerfChartStyle.ShowHorizontalGridLines.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgMAPvertlines))
            {
                cfgfileentries[ccfgMAPvertlines] = new List<string>() { pcMAP.PerfChartStyle.ShowVerticalGridLines.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgMAPupdatetime))
            {
                cfgfileentries[ccfgMAPupdatetime] = new List<string>() { cstarting_poll_speed.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgMAPtimermode))
            {
                cfgfileentries[ccfgMAPtimermode] = new List<string>() { Convert.ToInt32(cdefaulttimermode).ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgMAPgraphspacing))
            {
                cfgfileentries[ccfgMAPgraphspacing] = new List<string>() { cdefault_graph_spacing.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgMAPmaxentries))
            {
                cfgfileentries[ccfgMAPmaxentries] = new List<string>() { cdefault_max_graph_entries.ToString() };
            }

            
            if (!cfgfileentries.ContainsKey(ccfgthrottleanglename))
            {
                cfgfileentries[ccfgthrottleanglename] = new List<string>() { (String)pcThrottleAngle.Tag };
            }
            if (!cfgfileentries.ContainsKey(ccfgmaxthrottleangle))
            {
                cfgfileentries[ccfgmaxthrottleangle] = new List<string>() { max_throttle_angle.ToString(usculture) };
            }
            if (!cfgfileentries.ContainsKey(ccfgthrottleanglechartmode))
            {
                cfgfileentries[ccfgthrottleanglechartmode] = new List<string>() { Convert.ToInt32(pcThrottleAngle.ScaleMode).ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgthrottleangleavglineviewable))
            {
                cfgfileentries[ccfgthrottleangleavglineviewable] = new List<string>() { pcThrottleAngle.PerfChartStyle.ShowAverageLineViewable.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgthrottleangleavglinetotal))
            {
                cfgfileentries[ccfgthrottleangleavglinetotal] = new List<string>() { pcThrottleAngle.PerfChartStyle.ShowAverageLineTotal.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgthrottleanglehorizlines))
            {
                cfgfileentries[ccfgthrottleanglehorizlines] = new List<string>() { pcThrottleAngle.PerfChartStyle.ShowHorizontalGridLines.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgthrottleanglevertlines))
            {
                cfgfileentries[ccfgthrottleanglevertlines] = new List<string>() { pcThrottleAngle.PerfChartStyle.ShowVerticalGridLines.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgthrottleangleupdatetime))
            {
                cfgfileentries[ccfgthrottleangleupdatetime] = new List<string>() { cstarting_poll_speed.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgthrottleangletimermode))
            {
                cfgfileentries[ccfgthrottleangletimermode] = new List<string>() { Convert.ToInt32(cdefaulttimermode).ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgthrottleanglegraphspacing))
            {
                cfgfileentries[ccfgthrottleanglegraphspacing] = new List<string>() { cdefault_graph_spacing.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgthrottleanglemaxentries))
            {
                cfgfileentries[ccfgthrottleanglemaxentries] = new List<string>() { cdefault_max_graph_entries.ToString() };
            }

            
            if (!cfgfileentries.ContainsKey(ccfgRPMname))
            {
                cfgfileentries[ccfgRPMname] = new List<string>() { (String)pcRPM.Tag };
            }
            if (!cfgfileentries.ContainsKey(ccfgmaxrpm))
            {
                cfgfileentries[ccfgmaxrpm] = new List<string>() { max_rpm.ToString(usculture) };
            }
            if (!cfgfileentries.ContainsKey(ccfgRPMchartmode))
            {
                cfgfileentries[ccfgRPMchartmode] = new List<string>() { Convert.ToInt32(pcRPM.ScaleMode).ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgRPMavglineviewable))
            {
                cfgfileentries[ccfgRPMavglineviewable] = new List<string>() { pcRPM.PerfChartStyle.ShowAverageLineViewable.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgRPMavglinetotal))
            {
                cfgfileentries[ccfgRPMavglinetotal] = new List<string>() { pcRPM.PerfChartStyle.ShowAverageLineTotal.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgRPMhorizlines))
            {
                cfgfileentries[ccfgRPMhorizlines] = new List<string>() { pcRPM.PerfChartStyle.ShowHorizontalGridLines.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgRPMvertlines))
            {
                cfgfileentries[ccfgRPMvertlines] = new List<string>() { pcRPM.PerfChartStyle.ShowVerticalGridLines.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgRPMupdatetime))
            {
                cfgfileentries[ccfgRPMupdatetime] = new List<string>() { cstarting_poll_speed.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgRPMtimermode))
            {
                cfgfileentries[ccfgRPMtimermode] = new List<string>() { Convert.ToInt32(cdefaulttimermode).ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgRPMgraphspacing))
            {
                cfgfileentries[ccfgRPMgraphspacing] = new List<string>() { cdefault_graph_spacing.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgRPMmaxentries))
            {
                cfgfileentries[ccfgRPMmaxentries] = new List<string>() { cdefault_max_graph_entries.ToString() };
            }
            

            if (!cfgfileentries.ContainsKey(ccfginjectordurationname))
            {
                cfgfileentries[ccfginjectordurationname] = new List<string>() { (String)pcInjectorDuration.Tag };
            }
            if (!cfgfileentries.ContainsKey(ccfgmaxinjectorduration))
            {
                cfgfileentries[ccfgmaxinjectorduration] = new List<string>() { max_injector_duration.ToString(usculture) };
            }
            if (!cfgfileentries.ContainsKey(ccfginjectordurationchartmode))
            {
                cfgfileentries[ccfginjectordurationchartmode] = new List<string>() { Convert.ToInt32(pcInjectorDuration.ScaleMode).ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfginjectordurationavglineviewable))
            {
                cfgfileentries[ccfginjectordurationavglineviewable] = new List<string>() { pcInjectorDuration.PerfChartStyle.ShowAverageLineViewable.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfginjectordurationavglinetotal))
            {
                cfgfileentries[ccfginjectordurationavglinetotal] = new List<string>() { pcInjectorDuration.PerfChartStyle.ShowAverageLineTotal.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfginjectordurationhorizlines))
            {
                cfgfileentries[ccfginjectordurationhorizlines] = new List<string>() { pcInjectorDuration.PerfChartStyle.ShowHorizontalGridLines.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfginjectordurationvertlines))
            {
                cfgfileentries[ccfginjectordurationvertlines] = new List<string>() { pcInjectorDuration.PerfChartStyle.ShowVerticalGridLines.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfginjectordurationupdatetime))
            {
                cfgfileentries[ccfginjectordurationupdatetime] = new List<string>() { cstarting_poll_speed.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfginjectordurationtimermode))
            {
                cfgfileentries[ccfginjectordurationtimermode] = new List<string>() { Convert.ToInt32(cdefaulttimermode).ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfginjectordurationgraphspacing))
            {
                cfgfileentries[ccfginjectordurationgraphspacing] = new List<string>() { cdefault_graph_spacing.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfginjectordurationmaxentries))
            {
                cfgfileentries[ccfginjectordurationmaxentries] = new List<string>() { cdefault_max_graph_entries.ToString() };
            }
            

            if (!cfgfileentries.ContainsKey(ccfgignitionadvancename))
            {
                cfgfileentries[ccfgignitionadvancename] = new List<string>() { (String)pcIgnitionAdvance.Tag };
            }
            if (!cfgfileentries.ContainsKey(ccfgmaxignitionadvance))
            {
                cfgfileentries[ccfgmaxignitionadvance] = new List<string>() { max_ignition_advance.ToString(usculture) };
            }
            if (!cfgfileentries.ContainsKey(ccfgignitionadvancechartmode))
            {
                cfgfileentries[ccfgignitionadvancechartmode] = new List<string>() { Convert.ToInt32(pcIgnitionAdvance.ScaleMode).ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgignitionadvanceavglineviewable))
            {
                cfgfileentries[ccfgignitionadvanceavglineviewable] = new List<string>() { pcIgnitionAdvance.PerfChartStyle.ShowAverageLineViewable.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgignitionadvanceavglinetotal))
            {
                cfgfileentries[ccfgignitionadvanceavglinetotal] = new List<string>() { pcIgnitionAdvance.PerfChartStyle.ShowAverageLineTotal.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgignitionadvancehorizlines))
            {
                cfgfileentries[ccfgignitionadvancehorizlines] = new List<string>() { pcIgnitionAdvance.PerfChartStyle.ShowHorizontalGridLines.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgignitionadvancevertlines))
            {
                cfgfileentries[ccfgignitionadvancevertlines] = new List<string>() { pcIgnitionAdvance.PerfChartStyle.ShowVerticalGridLines.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgignitionadvanceupdatetime))
            {
                cfgfileentries[ccfgignitionadvanceupdatetime] = new List<string>() { cstarting_poll_speed.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgignitionadvancetimermode))
            {
                cfgfileentries[ccfgignitionadvancetimermode] = new List<string>() { Convert.ToInt32(cdefaulttimermode).ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgignitionadvancegraphspacing))
            {
                cfgfileentries[ccfgignitionadvancegraphspacing] = new List<string>() { cdefault_graph_spacing.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgignitionadvancemaxentries))
            {
                cfgfileentries[ccfgignitionadvancemaxentries] = new List<string>() { cdefault_max_graph_entries.ToString() };
            }

            
            if (!cfgfileentries.ContainsKey(ccfgmasterpollenabled))
            {
                cfgfileentries[ccfgmasterpollenabled] = new List<string>() { true.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgpollgraphspeed))
            {
                cfgfileentries[ccfgpollgraphspeed] = new List<string>() { cstarting_poll_speed.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgcheckforecuerrors))
            {
                cfgfileentries[ccfgcheckforecuerrors] = new List<string>() { true.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgcheckforecuerrorstime))
            {
                cfgfileentries[ccfgcheckforecuerrorstime] = new List<string>() { ccheck_for_errors_timer_interval.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgappwidth))
            {
                cfgfileentries[ccfgappwidth] = new List<string>() { capp_width.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgappheight))
            {
                cfgfileentries[ccfgappheight] = new List<string>() { capp_height.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgappX))
            {
                cfgfileentries[ccfgappX] = new List<string>() { this.Location.X.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgappY))
            {
                cfgfileentries[ccfgappY] = new List<string>() { this.Location.Y.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgbatteryvoltageloggingenabled))
            {
                cfgfileentries[ccfgbatteryvoltageloggingenabled] = new List<string>() { true.ToString() }; 
            }
            if (!cfgfileentries.ContainsKey(ccfgairtemploggingenabled))
            {
                cfgfileentries[ccfgairtemploggingenabled] = new List<string>() { true.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgwatertemploggingenabled))
            {
                cfgfileentries[ccfgwatertemploggingenabled] = new List<string>() { true.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgMAPloggingenabled))
            {
                cfgfileentries[ccfgMAPloggingenabled] = new List<string>() { true.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgthrottleangleloggingenabled))
            {
                cfgfileentries[ccfgthrottleangleloggingenabled] = new List<string>() { true.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgRPMloggingenabled))
            {
                cfgfileentries[ccfgRPMloggingenabled] = new List<string>() { true.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfginjectordurationloggingenabled))
            {
                cfgfileentries[ccfginjectordurationloggingenabled] = new List<string>() { true.ToString() };
            }
            if (!cfgfileentries.ContainsKey(ccfgignitionadvanceloggingenabled))
            {
                cfgfileentries[ccfgignitionadvanceloggingenabled] = new List<string>() { true.ToString() };
            }
        }
        #endregion

        #region UpdateFormFromCfgFile
        private void UpdateFormFromCfgFile()
        {
            this.Size = new Size(Convert.ToInt32(GetCfgFileEntry(ccfgappwidth)[0]), Convert.ToInt32(GetCfgFileEntry(ccfgappheight)[0]));
            this.Location = new Point(Convert.ToInt32(GetCfgFileEntry(ccfgappX)[0]), Convert.ToInt32(GetCfgFileEntry(ccfgappY)[0]));

            loaded = true;

            tsmiDebugAutomaticallyStart.Checked = Convert.ToBoolean(GetCfgFileEntry(cdebugautomaticallystart)[0]);
            if (tsmiDebugAutomaticallyStart.Checked)
            {
                tsmiDebugStart_Click(null, null);
            }

            cbMasterPollingEnabled.Checked = Convert.ToBoolean(GetCfgFileEntry(ccfgmasterpollenabled)[0]);
            nudPollSpeed.Value = Convert.ToDecimal(GetCfgFileEntry(ccfgpollgraphspeed)[0]);

            this.scGraphsHorizontal.SplitterDistance = Convert.ToInt32(GetCfgFileEntry(ccfgsplitterhorizontal)[0]);
            this.scGraphsTopLeft.SplitterDistance = Convert.ToInt32(GetCfgFileEntry(ccfgsplittertopleft)[0]);
            this.scGraphsTopRight.SplitterDistance = Convert.ToInt32(GetCfgFileEntry(ccfgsplittertopright)[0]);
            this.scGraphsTop.SplitterDistance = Convert.ToInt32(GetCfgFileEntry(ccfgsplittertop)[0]);
            this.scGraphsBottomLeft.SplitterDistance = Convert.ToInt32(GetCfgFileEntry(ccfgsplitterbottomleft)[0]);
            this.scGraphsBottomRight.SplitterDistance = Convert.ToInt32(GetCfgFileEntry(ccfgsplitterbottomright)[0]);
            this.scGraphsBottom.SplitterDistance = Convert.ToInt32(GetCfgFileEntry(ccfgsplitterbottom)[0]);

            max_battery_voltage = Convert.ToSingle(GetCfgFileEntry(ccfgmaxbatteryvoltage)[0], usculture);
            max_air_temp = Convert.ToSingle(GetCfgFileEntry(ccfgmaxairtemp)[0], usculture);
            max_water_temp = Convert.ToSingle(GetCfgFileEntry(ccfgmaxwatertemp)[0], usculture);
            max_MAP = Convert.ToSingle(GetCfgFileEntry(ccfgmaxMAP)[0], usculture);
            max_throttle_angle = Convert.ToSingle(GetCfgFileEntry(ccfgmaxthrottleangle)[0], usculture);
            max_rpm = Convert.ToSingle(GetCfgFileEntry(ccfgmaxrpm)[0], usculture);
            max_injector_duration = Convert.ToSingle(GetCfgFileEntry(ccfgmaxinjectorduration)[0], usculture);
            max_ignition_advance = Convert.ToSingle(GetCfgFileEntry(ccfgmaxignitionadvance)[0], usculture);
            master_poll_enable = Convert.ToBoolean(GetCfgFileEntry(ccfgmasterpollenabled)[0]);

            UpdatePCMaxValues();

            pcBatteryVoltage.ScaleMode = (SpPerfChart.ScaleMode)Convert.ToInt32(GetCfgFileEntry(ccfgbatteryvoltagechartmode)[0]);
            pcBatteryVoltage.PerfChartStyle.ShowAverageLineViewable = !Convert.ToBoolean(GetCfgFileEntry(ccfgbatteryvoltageavglineviewable)[0]);
            pcBatteryVoltage.PerfChartStyle.ShowAverageLineTotal = !Convert.ToBoolean(GetCfgFileEntry(ccfgbatteryvoltageavglinetotal)[0]);
            pcBatteryVoltage.PerfChartStyle.ShowHorizontalGridLines = !Convert.ToBoolean(GetCfgFileEntry(ccfgbatteryvoltagehorizlines)[0]);
            pcBatteryVoltage.PerfChartStyle.ShowVerticalGridLines = !Convert.ToBoolean(GetCfgFileEntry(ccfgbatteryvoltagevertlines)[0]);
            pcBatteryVoltage.TimerInterval = Convert.ToInt32(GetCfgFileEntry(ccfgbatteryvoltageupdatetime)[0]);
            pcBatteryVoltage.TimerMode = (SpPerfChart.TimerMode)Convert.ToInt32(GetCfgFileEntry(ccfgbatteryvoltagetimermode)[0]);
            pcBatteryVoltage.GridSpacing = Convert.ToInt32(GetCfgFileEntry(ccfgbatteryvoltagegraphspacing)[0]);
            pcBatteryVoltage.MaxNumValues = Convert.ToInt32(GetCfgFileEntry(ccfgbatteryvoltagemaxentries)[0]);

            pcAirTemp.ScaleMode = (SpPerfChart.ScaleMode)Convert.ToInt32(GetCfgFileEntry(ccfgairtempchartmode)[0]);
            pcAirTemp.PerfChartStyle.ShowAverageLineViewable = !Convert.ToBoolean(GetCfgFileEntry(ccfgairtempavglineviewable)[0]);
            pcAirTemp.PerfChartStyle.ShowAverageLineTotal = !Convert.ToBoolean(GetCfgFileEntry(ccfgairtempavglinetotal)[0]);
            pcAirTemp.PerfChartStyle.ShowHorizontalGridLines = !Convert.ToBoolean(GetCfgFileEntry(ccfgairtemphorizlines)[0]);
            pcAirTemp.PerfChartStyle.ShowVerticalGridLines = !Convert.ToBoolean(GetCfgFileEntry(ccfgairtempvertlines)[0]);
            pcAirTemp.TimerInterval = Convert.ToInt32(GetCfgFileEntry(ccfgairtempupdatetime)[0]);
            pcAirTemp.TimerMode = (SpPerfChart.TimerMode)Convert.ToInt32(GetCfgFileEntry(ccfgairtemptimermode)[0]);
            pcAirTemp.GridSpacing = Convert.ToInt32(GetCfgFileEntry(ccfgairtempgraphspacing)[0]);
            pcAirTemp.MaxNumValues = Convert.ToInt32(GetCfgFileEntry(ccfgairtempmaxentries)[0]);

            pcWaterTemp.ScaleMode = (SpPerfChart.ScaleMode)Convert.ToInt32(GetCfgFileEntry(ccfgwatertempchartmode)[0]);
            pcWaterTemp.PerfChartStyle.ShowAverageLineViewable = !Convert.ToBoolean(GetCfgFileEntry(ccfgwatertempavglineviewable)[0]);
            pcWaterTemp.PerfChartStyle.ShowAverageLineTotal = !Convert.ToBoolean(GetCfgFileEntry(ccfgwatertempavglinetotal)[0]);
            pcWaterTemp.PerfChartStyle.ShowHorizontalGridLines = !Convert.ToBoolean(GetCfgFileEntry(ccfgwatertemphorizlines)[0]);
            pcWaterTemp.PerfChartStyle.ShowVerticalGridLines = !Convert.ToBoolean(GetCfgFileEntry(ccfgwatertempvertlines)[0]);
            pcWaterTemp.TimerInterval = Convert.ToInt32(GetCfgFileEntry(ccfgwatertempupdatetime)[0]);
            pcWaterTemp.TimerMode = (SpPerfChart.TimerMode)Convert.ToInt32(GetCfgFileEntry(ccfgwatertemptimermode)[0]);
            pcWaterTemp.GridSpacing = Convert.ToInt32(GetCfgFileEntry(ccfgwatertempgraphspacing)[0]);
            pcWaterTemp.MaxNumValues = Convert.ToInt32(GetCfgFileEntry(ccfgwatertempmaxentries)[0]);
            
            pcMAP.ScaleMode = (SpPerfChart.ScaleMode)Convert.ToInt32(GetCfgFileEntry(ccfgMAPchartmode)[0]);
            pcMAP.PerfChartStyle.ShowAverageLineViewable = !Convert.ToBoolean(GetCfgFileEntry(ccfgMAPavglineviewable)[0]);
            pcMAP.PerfChartStyle.ShowAverageLineTotal = !Convert.ToBoolean(GetCfgFileEntry(ccfgMAPavglinetotal)[0]);
            pcMAP.PerfChartStyle.ShowHorizontalGridLines = !Convert.ToBoolean(GetCfgFileEntry(ccfgMAPhorizlines)[0]);
            pcMAP.PerfChartStyle.ShowVerticalGridLines = !Convert.ToBoolean(GetCfgFileEntry(ccfgMAPvertlines)[0]);
            pcMAP.TimerInterval = Convert.ToInt32(GetCfgFileEntry(ccfgMAPupdatetime)[0]);
            pcMAP.TimerMode = (SpPerfChart.TimerMode)Convert.ToInt32(GetCfgFileEntry(ccfgMAPtimermode)[0]);
            pcMAP.GridSpacing = Convert.ToInt32(GetCfgFileEntry(ccfgMAPgraphspacing)[0]);
            pcMAP.MaxNumValues = Convert.ToInt32(GetCfgFileEntry(ccfgMAPmaxentries)[0]);
            
            pcThrottleAngle.ScaleMode = (SpPerfChart.ScaleMode)Convert.ToInt32(GetCfgFileEntry(ccfgthrottleanglechartmode)[0]);
            pcThrottleAngle.PerfChartStyle.ShowAverageLineViewable = !Convert.ToBoolean(GetCfgFileEntry(ccfgthrottleangleavglineviewable)[0]);
            pcThrottleAngle.PerfChartStyle.ShowAverageLineTotal = !Convert.ToBoolean(GetCfgFileEntry(ccfgthrottleangleavglinetotal)[0]);
            pcThrottleAngle.PerfChartStyle.ShowHorizontalGridLines = !Convert.ToBoolean(GetCfgFileEntry(ccfgthrottleanglehorizlines)[0]);
            pcThrottleAngle.PerfChartStyle.ShowVerticalGridLines = !Convert.ToBoolean(GetCfgFileEntry(ccfgthrottleanglevertlines)[0]);
            pcThrottleAngle.TimerInterval = Convert.ToInt32(GetCfgFileEntry(ccfgthrottleangleupdatetime)[0]);
            pcThrottleAngle.TimerMode = (SpPerfChart.TimerMode)Convert.ToInt32(GetCfgFileEntry(ccfgthrottleangletimermode)[0]);
            pcThrottleAngle.GridSpacing = Convert.ToInt32(GetCfgFileEntry(ccfgthrottleanglegraphspacing)[0]);
            pcThrottleAngle.MaxNumValues = Convert.ToInt32(GetCfgFileEntry(ccfgthrottleanglemaxentries)[0]);

            pcRPM.ScaleMode = (SpPerfChart.ScaleMode)Convert.ToInt32(GetCfgFileEntry(ccfgRPMchartmode)[0]);
            pcRPM.PerfChartStyle.ShowAverageLineViewable = !Convert.ToBoolean(GetCfgFileEntry(ccfgRPMavglineviewable)[0]);
            pcRPM.PerfChartStyle.ShowAverageLineTotal = !Convert.ToBoolean(GetCfgFileEntry(ccfgRPMavglinetotal)[0]);
            pcRPM.PerfChartStyle.ShowHorizontalGridLines = !Convert.ToBoolean(GetCfgFileEntry(ccfgRPMhorizlines)[0]);
            pcRPM.PerfChartStyle.ShowVerticalGridLines = !Convert.ToBoolean(GetCfgFileEntry(ccfgRPMvertlines)[0]);
            pcRPM.TimerInterval = Convert.ToInt32(GetCfgFileEntry(ccfgRPMupdatetime)[0]);
            pcRPM.TimerMode = (SpPerfChart.TimerMode)Convert.ToInt32(GetCfgFileEntry(ccfgRPMtimermode)[0]);
            pcRPM.GridSpacing = Convert.ToInt32(GetCfgFileEntry(ccfgRPMgraphspacing)[0]);
            pcRPM.MaxNumValues = Convert.ToInt32(GetCfgFileEntry(ccfgRPMmaxentries)[0]);

            pcInjectorDuration.ScaleMode = (SpPerfChart.ScaleMode)Convert.ToInt32(GetCfgFileEntry(ccfginjectordurationchartmode)[0]);
            pcInjectorDuration.PerfChartStyle.ShowAverageLineViewable = !Convert.ToBoolean(GetCfgFileEntry(ccfginjectordurationavglineviewable)[0]);
            pcInjectorDuration.PerfChartStyle.ShowAverageLineTotal = !Convert.ToBoolean(GetCfgFileEntry(ccfginjectordurationavglinetotal)[0]);
            pcInjectorDuration.PerfChartStyle.ShowHorizontalGridLines = !Convert.ToBoolean(GetCfgFileEntry(ccfginjectordurationhorizlines)[0]);
            pcInjectorDuration.PerfChartStyle.ShowVerticalGridLines = !Convert.ToBoolean(GetCfgFileEntry(ccfginjectordurationvertlines)[0]);
            pcInjectorDuration.TimerInterval = Convert.ToInt32(GetCfgFileEntry(ccfginjectordurationupdatetime)[0]);
            pcInjectorDuration.TimerMode = (SpPerfChart.TimerMode)Convert.ToInt32(GetCfgFileEntry(ccfginjectordurationtimermode)[0]);
            pcInjectorDuration.GridSpacing = Convert.ToInt32(GetCfgFileEntry(ccfginjectordurationgraphspacing)[0]);
            pcInjectorDuration.MaxNumValues = Convert.ToInt32(GetCfgFileEntry(ccfginjectordurationmaxentries)[0]);

            pcIgnitionAdvance.ScaleMode = (SpPerfChart.ScaleMode)Convert.ToInt32(GetCfgFileEntry(ccfgignitionadvancechartmode)[0]);
            pcIgnitionAdvance.PerfChartStyle.ShowAverageLineViewable = !Convert.ToBoolean(GetCfgFileEntry(ccfgignitionadvanceavglineviewable)[0]);
            pcIgnitionAdvance.PerfChartStyle.ShowAverageLineTotal = !Convert.ToBoolean(GetCfgFileEntry(ccfgignitionadvanceavglinetotal)[0]);
            pcIgnitionAdvance.PerfChartStyle.ShowHorizontalGridLines = !Convert.ToBoolean(GetCfgFileEntry(ccfgignitionadvancehorizlines)[0]);
            pcIgnitionAdvance.PerfChartStyle.ShowVerticalGridLines = !Convert.ToBoolean(GetCfgFileEntry(ccfgignitionadvancevertlines)[0]);
            pcIgnitionAdvance.TimerInterval = Convert.ToInt32(GetCfgFileEntry(ccfgignitionadvanceupdatetime)[0]);
            pcIgnitionAdvance.TimerMode = (SpPerfChart.TimerMode)Convert.ToInt32(GetCfgFileEntry(ccfgignitionadvancetimermode)[0]);
            pcIgnitionAdvance.GridSpacing = Convert.ToInt32(GetCfgFileEntry(ccfgignitionadvancegraphspacing)[0]);
            pcIgnitionAdvance.MaxNumValues = Convert.ToInt32(GetCfgFileEntry(ccfgignitionadvancemaxentries)[0]);

            cbBatteryVoltage.Checked = Convert.ToBoolean(GetCfgFileEntry(ccfgbatteryvoltageloggingenabled)[0]);
            cbAirTemp.Checked = Convert.ToBoolean(GetCfgFileEntry(ccfgairtemploggingenabled)[0]);
            cbWaterTemp.Checked = Convert.ToBoolean(GetCfgFileEntry(ccfgwatertemploggingenabled)[0]);
            cbMAP.Checked = Convert.ToBoolean(GetCfgFileEntry(ccfgMAPloggingenabled)[0]);
            cbThrottleAngle.Checked = Convert.ToBoolean(GetCfgFileEntry(ccfgthrottleangleloggingenabled)[0]);
            cbRPM.Checked = Convert.ToBoolean(GetCfgFileEntry(ccfgRPMloggingenabled)[0]);
            cbInjectorDuration.Checked = Convert.ToBoolean(GetCfgFileEntry(ccfginjectordurationloggingenabled)[0]);
            cbIgnitionAdvance.Checked = Convert.ToBoolean(GetCfgFileEntry(ccfgignitionadvanceloggingenabled)[0]);
            cbFuelTrimPowerOn.Checked = false;
            cbFuelTrimRaw.Checked = false;
            cbSpare1.Checked = false;
            cbSpare2.Checked = false;
            cbStartEnrichment.Checked = false;

            SetPCsTimerMode(true);

            foreach (SpPerfChart.PerfChart pc in perfcharts)
            {
                SetNewPCChartMode(pc, pc.ScaleMode.ToString());
                SetNewPCAvgLineViewable(pc);
                SetNewPCAvgLineTotal(pc);
                SetNewPCHorizLines(pc);
                SetNewPCVertLines(pc);
                SetNewPCTimerInterval(pc, pc.TimerInterval);
                //SetNewPCTimerMode(pc, pc.TimerMode.ToString());
                SetNewPCGraphSpacing(pc, pc.GridSpacing);
                SetNewPCMaxEntries(pc, pc.MaxNumValues);

                pc.HorizontalScroll.Enabled = true;
                pc.HorizontalScroll.Visible = true;

                pc.Clear();
            }

            check_for_ecu_errors_when_connected = Convert.ToBoolean(GetCfgFileEntry(ccfgcheckforecuerrors)[0]);
            if (check_for_ecu_errors_when_connected)
            {
                check_for_ecu_errors_when_connected = false;
                tsmiECUErrorsCheckForECUErrorsAtInit_Click(null, null);
            }
            check_for_errors = false;
            //check_for_errors_timer.Interval = Convert.ToInt32(GetCfgFileEntry(ccfgcheckforecuerrorstime)[0]);
            //check_for_errors_timer.Tick += new EventHandler(CheckForErrorsTimerTick);
            tsmiECUErrorsCheckForECUErrorsAtInit.ToolTipText = "When checked the ECU will be interrogated for any errors present " + check_for_errors_timer.Interval.ToString() + "ms after it has been initialized";
        }
        #endregion

        #region Get Cfg file entry
        private List<String> GetCfgFileEntry(String key)
        {
            if (cfgfileentries.ContainsKey(key))
            {
                return cfgfileentries[key];
            }
            else
            {
                return new List<String>();
            }
        }
        #endregion

        #region Set cfg file entry
        private void SetCfgFileEntry(String entry, List<String> values)
        {
            if (!cfgfileentries.ContainsKey(entry))
            {
                cfgfileentries[entry] = new List<string>();
            }
            cfgfileentries[entry] = values;
        }
        #endregion

        #region Write cfg file
        private bool WriteCfgFile()
        {
            if (cfgfilepath == null || cfgfilepath == "")
            {
                return true;
            }

            FileStream fs = null;
            try
            {
                fs = File.Open(cfgfilepath, FileMode.Create, FileAccess.ReadWrite);
            }
            catch (Exception e)
            {
                Trace.WriteLine("Unable to open " + cfgfilepath + " to update entries\r\nException is:\r\n" + e.Message);
                return false;
            }

            StreamWriter sw = new StreamWriter(fs);

            try
            {
                foreach (String key in cfgfileentries.Keys)
                {
                    if (key.StartsWith("[") && key.EndsWith("]"))
                    {
                        sw.WriteLine(key);

                        foreach (String line in cfgfileentries[key])
                        {
                            sw.WriteLine(line);
                        }

                        String endheader = key.Replace("]", "_END]");
                        sw.WriteLine(endheader);
                    }
                    else
                    {
                        foreach (String line in cfgfileentries[key])
                        {
                            sw.WriteLine(key + "=" + line);
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Trace.WriteLine("Exception while writing cfg file\r\nException is " + e.Message);
                return false;
            }
            finally
            {
                if (sw != null)
                {
                    sw.Close();
                    sw = null;
                }

                if (fs != null)
                {
                    fs.Dispose();
                    fs = null;
                }
            }

            Trace.WriteLine(cfgfilepath + " updated");

            return true;
        }
        #endregion

        #region PlaySound
        // for playing sounds
        public bool PlaySound(String soundpath, float volume)
        {
            if (File.Exists(soundpath) && device != null)
            {
                try
                {
                    BufferDescription bd = new BufferDescription();
                    bd.GlobalFocus = false;
                    bd.ControlVolume = true;
                    Buffer sound = new Buffer(soundpath, bd, device);
                    //sound = new SecondaryBuffer(soundpath, bd, device);
                    if (volume < 0)
                    {
                        volume = 0;
                    }
                    else if (volume > 1)
                    {
                        volume = 1;
                    }
                    //int vol = Convert.ToInt32(Math.Round(-5000 * Math.Log10(1000 - volume)));
                    int vol = -10000 + Convert.ToInt32(volume * 10000);
                    sound.Volume = vol;
                    sound.Play(0, Microsoft.DirectX.DirectSound.BufferPlayFlags.Default);
                }
                catch
                {
                    // do nothing as probably not supported sound type passed
                    return false;
                }
                return true;
            }
            return false;
        }

        public bool PlaySound(String soundpath)
        {
            if (File.Exists(soundpath) && device != null)
            {
                try
                {
                    Buffer sound = new Buffer(soundpath, device);
                    //sound = new SecondaryBuffer(soundpath, device);
                    sound.Play(0, Microsoft.DirectX.DirectSound.BufferPlayFlags.Default);
                }
                catch
                {
                    return false;
                }
                return true;
            }
            return false;
        }
        #endregion

        #region Destroy
        private void Destroy()
        {
            if (device != null)
            {
                device.Dispose();
                device = null;
            }
        }
        #endregion
    }
}
