private void btnStartStop_Click(object sender, EventArgs e)
        {
            if (!CanTool.IsCommunication)
            {
                return;
            }

            if (stage == scanStage.pause)
            {
                stage = scanStage.waitingForCanSleep;

                // id range
                idStart   = numIdFrom.Value;
                idStop    = numIdTo.Value;
                idCurrent = idStart;
                byte[] d = Tools.hexStringToByteArray(tbData.Text);

                if (d == null || d.Length == 0 || d.Length > canMessage.maxDataBytesNum())
                {
                    return;
                }

                // int only
                messageCountToSend = Convert.ToInt32(tbCount.Text, 10);
                butSleepTmo        = Convert.ToInt32(tbBusSleep.Text, 10);
                idCheckTmo         = Convert.ToInt32(tbIdCheck.Text, 10);

                message = new canMessage2((int)idCurrent, rb29BitId.Checked, d, 0);

                // trace
                if (!string.IsNullOrEmpty(tbTrace.Text))
                {
                    trace(Environment.NewLine);
                }

                trace(Environment.NewLine + string.Format("Scan from {0} to {1}",
                                                          canMessage.getCanIdString((int)idStart, rb29BitId.Checked),
                                                          canMessage.getCanIdString((int)idStop, rb29BitId.Checked)));
                trace(string.Format("DLC = {0}, Data = {1}",
                                    message.Id.GetDlcAsString(),
                                    message.GetDataString(" ")));
                trace("Waiting until the CAN bus is sleeping");

                worker      = new Thread(onWorker);
                worker.Name = "Wrk";
                worker.Start();
            }
            else
            {
                // stop
                stage = scanStage.pause;
                trace("Aborted");
            }

            guiStartStop();
        }
        private void onWorker()
        {
            // we're using this time for can sleep detection
            DateTime dtScanStarted = DateTime.Now;
            // and this one for trace only
            DateTime dtTrace = DateTime.Now;

            while (stage != scanStage.pause)
            {
                if (stage == scanStage.waitingForCanSleep)
                {
                    var diffInSeconds = (DateTime.Now - dtLastCanMsg).TotalMilliseconds;
                    if (diffInSeconds > butSleepTmo)
                    {
                        stage         = scanStage.scanning;
                        dtScanStarted = DateTime.Now;
                        continue;
                    }

                    Thread.Sleep(500);
                }
                else if (stage == scanStage.scanning)
                {
                    // send
                    CanTool.SendCanMessage(message, messageCountToSend);
                    trace(idCurrent);

                    // wait
                    Thread.Sleep(messageCountToSend + messageCountToSend / 2);

                    // check date time
                    if (dtScanStarted < dtLastCanMsg)
                    {
                        trace("A new CAN message has received");
                        trace("Waiting untill the CAN bus is sleeping again");
                        // a message has received
                        stage = scanStage.waitingForCanSleepAfterScanning;
                        continue;
                    }

                    // update
                    if (idCurrent == idStop)
                    {
                        stage = scanStage.pause;
                        trace("Failed. The low border has reached.");
                        continue;
                    }
                    else
                    {
                        idCurrent = idStart > idStop ? idCurrent - 1 : idCurrent + 1;
                        //message.Id.Id = (int)idCurrent;
                        message = new canMessage2((int)idCurrent, message.Id.Is29bit, message.Data);
                    }
                }
                else if (stage == scanStage.waitingForCanSleepAfterScanning)
                {
                    var diffInSeconds = (DateTime.Now - dtLastCanMsg).TotalMilliseconds;
                    if (diffInSeconds > butSleepTmo)
                    {
                        stage         = scanStage.checking;
                        dtScanStarted = DateTime.Now;
                        trace("Can Bus is sleeping again");
                        continue;
                    }

                    Thread.Sleep(500);
                }
                else if (stage == scanStage.checking)
                {
                    // send data in the reverse direction
                    CanTool.SendCanMessage(message, messageCountToSend);
                    trace(idCurrent);


                    // wait for idCheckTmo msec
                    trace("Waiting for " + idCheckTmo.ToString() + " msec");
                    Thread.Sleep(idCheckTmo);

                    // check date time
                    if (dtScanStarted < dtLastCanMsg)
                    {
                        // a message has received
                        trace("Done");
                        trace(string.Format("Activation ID is: {0}", message.Id.GetIdAsString()));
                        stage = scanStage.pause;
                        continue;
                    }

                    idCurrent = idStart > idStop ? idCurrent + 1 : idCurrent - 1;
                    //message.Id = (int)idCurrent;
                    message = new canMessage2((int)idCurrent, message.Id.Is29bit, message.Data);
                }
            }

            double elapsed  = (DateTime.Now - dtTrace).TotalMilliseconds / 1000.0d;;
            string sElapsed = elapsed.ToString("0.000");

            trace(string.Format("Elapsed Time: {0} sec", sElapsed));

            // end of while
            guiStartStop();
        }