public int Pause()
        {
            ControlStatuses status;

            lock (pvars)
            {
                status = this.deviceStatus;
            }
            if ((status & ControlStatuses.Paused) == ControlStatuses.Paused)
            {
                return(2);
            }
            else if (!((status & ControlStatuses.Running) == ControlStatuses.Running))
            {
                return(4);
            }
            else
            {
                status |= ControlStatuses.Paused;
                lock (pvars)
                {
                    this.deviceStatus = status;
                }
                return(0);
            }
        }
        public int Stop()
        {
            ControlStatuses status;

            lock (pvars)
            {
                status = this.deviceStatus;
            }
            lock (pvars)
            {
                deviceStatus = ControlStatuses.Terminate;
            }
            return(0);
        }
        /* ----------------------------------------------------------------------
         * EXECUTIVE FUNCTIONS
         *
         * start() - start/re-start reading telemetry in a thread
         * stop() - stop reading telemetry and terminates thread
         * pause() - discards inbound telemetry (ignores it)
         *
         *
         * THE MEAT OF THE CODE IS IN RUN() IT IS A WHILE LOOP CONSTANTLY
         * READING TELEMETRY AND ISSUING CONTROL COMMANDS WHILST UPDATING
         * MEMBER VARIABLES AS TELEMETRY CHANGES ARE FOUND.
         *
         * run() - bg thread continuosly reading/writing the device port
         *         it is kicked off by start and then examines status to check
         *         when it is time to pause or stop altogether.
         * ---------------------------------------------------------------------- */
        public int Restart()
        {
            ControlStatuses status;

            lock (pvars)
            {
                status = this.deviceStatus;
            }
            if ((status & ControlStatuses.Running) == ControlStatuses.Running && (status & ControlStatuses.Paused) == ControlStatuses.Paused)
            {
                status &= ControlStatuses.Paused;
                lock (pvars)
                {
                    this.deviceStatus = status;
                }
                return(0);
            }
            return(2);
        }
        public Computrainer(string device)
        {
            devicePower = deviceHeartRate = deviceCadence = deviceSpeed = deviceRRC = 0;
            for (int i = 0; i < 24; i++)
            {
                spinData[i] = 0;
            }
            mode                   = DefaultMode;
            load                   = DefaultLoad;
            gradient               = DefaultGradient;
            deviceCalibrated       = false;
            deviceHRConnected      = false;
            deviceCadenceConnected = false;
            Device                 = device;
            deviceStatus           = 0;

            Buffer.BlockCopy(ERGO_Command, 0, ergo_command, 0, 56);
            Buffer.BlockCopy(SS_Command, 0, ss_command, 0, 56);
        }
        public void Run()
        {
            // locally cached settings - only update main class variables
            //                           when they change

            int cmds = 0;            // count loops with no command sent

            // holders for unpacked telemetry
            int ss1, ss2, ss3, buttons, type, value8, value12;

            ss1 = 0; ss2 = 0; ss3 = 0; buttons = 0; type = 0; value8 = 0; value12 = 0;

            // newly read values - compared against cached values
            int changed;
            DeviceOperationModes newmode;
            double newload, newgradient;
            double newspeed, newRRC;
            bool   newcalibrated, newhrconnected, newcadconnected;
            bool   isDeviceOpen = false;

            // Cached current values
            // when new values are received from the device
            // if they differ from current values we update
            // otherwise do nothing
            DeviceOperationModes curmode;
            ControlStatuses      curStatus;
            double curload, curgradient;
            double curPower;                       // current output power in Watts
            double curHeartRate;                   // current heartrate in BPM
            double curCadence;                     // current cadence in RPM
            double curSpeed;                       // current speef in KPH
            double curRRC;                         // calibrated Rolling Resistance
            bool   curcalibrated;                  // is it calibrated?
            bool   curhrconnected;                 // is HR sensor connected?
            bool   curcadconnected;                // is CAD sensor connected?

            double[] curspinData = new double[24]; // SS values only in SS_MODE
            int      curButtons;                   // Button status


            // initialise local cache & main vars
            lock (pvars)
            {
                this.deviceStatus           = ControlStatuses.Running;
                curmode                     = this.mode;
                curload                     = this.load;
                curgradient                 = this.gradient;
                curPower                    = this.devicePower = 0;
                curHeartRate                = this.deviceHeartRate = 0;
                curCadence                  = this.deviceCadence = 0;
                curSpeed                    = this.deviceSpeed = 0;
                curButtons                  = this.deviceButtons;
                curRRC                      = this.deviceRRC = 0;
                curcalibrated               = false;
                this.deviceCalibrated       = false;
                curhrconnected              = false;
                this.deviceHRConnected      = false;
                curcadconnected             = false;
                this.deviceCadenceConnected = false;
                curButtons                  = 0;
                this.deviceButtons          = 0;
                curStatus                   = this.deviceStatus;
                for (int i = 0; i < 24; i++)
                {
                    curspinData[i] = this.spinData[i] = 0;
                }
            }


            // open the device
            if (!OpenPort())
            {
                return; // open failed!
            }
            else
            {
                isDeviceOpen = true;
            }

            // send first command to get computrainer ready
            PrepareCommand(curmode, curmode == DeviceOperationModes.Ergo ? curload : curgradient);
            if (SendCommand(curmode))
            {
                while (true)
                {
                    if (isDeviceOpen)
                    {
                        if (ReadMessage() > 0)
                        {
                            changed = 0;
                            UnpackTelemetry(ref ss1, ref ss2, ref ss3, ref buttons, ref type, ref value8, ref value12);
                            switch (type)
                            {
                            case (int)MessageTypes.Speed:
                                value12  *= 36;         // convert from mps to kph
                                value12  *= 9;
                                value12  /= 10;         // it seems that compcs takes off 10% ????
                                newspeed  = value12;
                                newspeed /= 1000;
                                if (newspeed != curSpeed)
                                {
                                    lock (pvars)
                                    {
                                        this.deviceSpeed = curSpeed = newspeed;
                                    }
                                    changed = 1;
                                }
                                break;

                            case (int)MessageTypes.Power:
                                if (value12 != curPower)
                                {
                                    curPower = value12;
                                    lock (pvars)
                                    {
                                        this.devicePower = curPower;
                                    }
                                    changed = 1;
                                }
                                break;

                            case (int)MessageTypes.HeartRate:
                                if (value8 != curHeartRate)
                                {
                                    curHeartRate = value8;
                                    lock (pvars)
                                    {
                                        this.deviceHeartRate = curHeartRate;
                                    }
                                    changed = 1;
                                }
                                break;

                            case (int)MessageTypes.Cadence:
                                if (value8 != curCadence)
                                {
                                    curCadence = value8;
                                    lock (pvars)
                                    {
                                        this.deviceCadence = curCadence;
                                    }

                                    changed = 1;
                                }
                                break;

                            case (int)MessageTypes.RRC:
                                newcalibrated = (value12 & 2048) != 0 ? true : false;
                                newRRC        = value12 & ~2048; // only use 11bits
                                newRRC       /= 256;

                                if (newRRC != curRRC)
                                {
                                    lock (pvars)
                                    {
                                        this.deviceRRC = curRRC = newRRC;
                                    }
                                    changed = 1;
                                }
                                break;

                            case (int)MessageTypes.Sensor:
                                newcadconnected = (value12 & 2048) != 0 ? true : false;
                                newhrconnected  = (value12 & 1024) != 0 ? true : false;

                                if (newhrconnected != curhrconnected || newcadconnected != curcadconnected)
                                {
                                    lock (pvars)
                                    {
                                        this.deviceHRConnected      = curhrconnected = newhrconnected;
                                        this.deviceCadenceConnected = curcadconnected = newcadconnected;
                                    }
                                    changed = 1;
                                }
                                break;

                            default:
                                break;
                            }
                            if (buttons != curButtons)
                            {
                                lock (pvars)
                                {
                                    this.deviceButtons = curButtons = buttons;
                                }
                            }
                            else
                            {
                                Thread.Sleep(100);
                            }
                            lock (pvars)
                            {
                                curStatus   = this.deviceStatus;
                                newmode     = this.mode;
                                newload     = this.load;
                                curgradient = newgradient = this.gradient;
                            }
                            if ((curStatus & ControlStatuses.Running) == 0)
                            {
                                ClosePort();
                                return;
                            }
                            if ((curStatus & ControlStatuses.Paused) != 0 && isDeviceOpen)
                            {
                                ClosePort();
                                isDeviceOpen = false;
                            }
                            else if ((curStatus & ControlStatuses.Paused) == 0 && (curStatus & ControlStatuses.Running) != 0 && isDeviceOpen == false)
                            {
                                if (!OpenPort())
                                {
                                    return;
                                }
                                isDeviceOpen = true;
                                PrepareCommand(curmode, curmode == DeviceOperationModes.Ergo ? curload : curgradient);
                                if (!SendCommand(curmode))
                                {
                                    ClosePort();
                                    return;
                                }
                                if (isDeviceOpen && (cmds % 10) == 0)
                                {
                                    cmds        = 1;
                                    curmode     = newmode;
                                    curload     = newload;
                                    curgradient = newgradient;
                                    PrepareCommand(curmode, curmode == DeviceOperationModes.Ergo ? curload : curgradient);
                                    if (!SendCommand(curmode))
                                    {
                                        ClosePort();
                                        cmds = 10;
                                        return;
                                    }
                                }
                                else
                                {
                                    cmds++;
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                ClosePort();
                return;
            }
        }