private Status CheckAsyncWebServerResp(AsyncWebExchange asyncWebExchange, int timeoutMs, out bool isDone)
        {
            string response = string.Empty;
            Status retval   = Status.Ok;

            isDone = false;

            /* async web response handler  */
            retval = asyncWebExchange.PollResponse(timeoutMs, out response);
            if (retval == Status.AsyncWebReqNotDone)
            {
                /* this is okay, its just not done yet */
                retval = Status.Ok;
            }
            else if (retval != Status.Ok)
            {
                /* let caller something went wrong  */
                _firmwareStatus.progressPerc = 100;
                _firmwareStatus.message      = "Received Error" + retval;
                isDone = true;
            }
            else
            {
                /*  we are done - parse firmware-update resp */
                FirmwareUpdateReturn jsonDeser = JsonConvert.DeserializeObject <FirmwareUpdateReturn>(response);
                retval = (Status)jsonDeser.GeneralReturn.Error;

                /* save status update so calling application can poll it */
                if (retval == Status.Ok)
                {
                    _firmwareStatus.progressPerc = 100;
                    _firmwareStatus.message      = "Successful Firmware Update";
                }
                else
                {
                    _firmwareStatus.progressPerc = 100;
                    _firmwareStatus.message      = jsonDeser.UpdateMessage;
                }

                /* let caller know the field upgrade did stop/finish */
                isDone = true;
            }

            return(retval);
        }
        private Status ExecuteFieldUpgrade(DeviceDescrip ddRef, AsyncWebExchange asyncWebExchange, string fileName, bool usingSftp)
        {
            Status retval   = Status.Ok;
            string response = string.Empty;

            /* let user know action is being processed */
            SetFieldUpgradeStatus("Reading CRF...", 0);


            byte[] fileContents = null;
            /* If we're using POST, we need to make sure the file has contents */
            if (!usingSftp)
            {
                /* copy out CRF */
                fileContents = File.Read(_action.filePath);

                /* check file read */
                if (retval == Status.Ok)
                {
                    if (fileContents == null)
                    {
                        retval = Status.CouldNotOpenFile;
                    }
                }
            }

            /* check firmUpdate progress first */
            if (retval == Status.Ok)
            {
                SetFieldUpgradeStatus("Confirm server is ready", 0); /* we are confirming if field upgrade is already occuring */
                /* Make double sure that we aren't field upgrading. Server already does this but there's no harm in checking again */
                retval = ConfirmIfFieldUpgradeIsOccuring(ddRef, false);
                SetFieldUpgradeStatus("Confirm server is ready : " + retval, 0); /* display the result of this check */
            }

            /* request firmware-update of device */
            if (retval == Status.Ok)
            {
                SetFieldUpgradeStatus("Starting field-upgrade", 0); /* now we will request to start a new FU session */

                /* If we're using sftp, use Get */
                if (usingSftp)
                {
                    /* start firmware-update */
                    retval = asyncWebExchange.StartHttpGet(_hostName, ddRef.model, ddRef.deviceID, ActionType.FieldUpgradeDevice, 60000, "&file=" + fileName);
                }
                else /* Otherwise use Post */
                {
                    /* start firmware-update */
                    retval = asyncWebExchange.StartHttpPost(_hostName, ddRef.model, ddRef.deviceID, ActionType.FieldUpgradeDevice, fileContents, 60000);
                }
            }
            /* confirm FU started okay */
            if (retval == Status.Ok)
            {
                /* wait until we get the rising edge of the flash event - a percent update with healthy error code, or a failed response from async web request. */
                int       i         = 0;
                const int kMaxLoops = 50;
                for (; i < kMaxLoops; ++i)
                {
                    const int kTimePerLoopMs = 100;

                    /* check if flashing has started */
                    retval = ConfirmIfFieldUpgradeIsOccuring(ddRef, true);

                    /* leave for-loop if flashing started */
                    if (retval == Status.Ok)
                    {
                        break;
                    }

                    /* wait for kTimePerLoopMs to see if firmUpdate finishes early */
                    bool bIsDone = false;
                    var  respErr = CheckAsyncWebServerResp(asyncWebExchange, kTimePerLoopMs, out bIsDone);

                    /* leave for-loop if FU request completed */
                    if (bIsDone)
                    {
                        /* since we are leaving the loop because of this, save the error code */
                        retval = respErr;
                        break;
                    }
                }
                /* uncomment to help better debug where errors are sourced */
                //SetFieldUpgradeStatus("Starting field-upgrade : " + retval, 0);
            }
            /* poll status - loop while status is ok and firmUpdate is not complete*/
            int notUpdatingCnt = 0;

            while (retval == Status.Ok)
            {
                /* request an update */
                if (retval == Status.Ok)
                {
                    retval = _WebServerScripts.HttpGet(_hostName, ddRef.model, ddRef.deviceID, ActionType.CheckUpdateProgress, out response, "", 200);
                }
                /* proces the update response */
                if (retval == Status.Ok)
                {
                    /* parse the response that has the field-upgrade status */
                    ProgressReturn jsonResp = JsonConvert.DeserializeObject <ProgressReturn>(response);
                    retval = (Status)jsonResp.GeneralReturn.Error;

                    if (retval == Status.CTRE_DI_NotUpdating)
                    {
                        ++notUpdatingCnt; /* most likely flash has finished */
                        retval = Status.Ok;
                    }
                    else
                    {
                        /* save it so calling application can poll it */
                        SetFieldUpgradeStatus(jsonResp);
                    }
                }
                /* check on the first async web exchange */
                if (retval == Status.Ok)
                {
                    bool bIsDone = false;
                    retval = CheckAsyncWebServerResp(asyncWebExchange, 100, out bIsDone);

                    /* leave while-loop if FU request completed */
                    if (bIsDone)
                    {
                        break;
                    }
                }
                /* check on our state in case GUI is shutting down */
                if (GetState_NoLock() != State.ExecAction)
                {
                    retval = Status.Aborted;
                }
            }
            return(retval);
        }
        /// <summary>
        /// Service _action and update _action.Error.
        /// </summary>
        private Status PerformAction(out bool setStateToConnecting)
        {
            setStateToConnecting = false;
            /* get info on this device */
            byte devID = _action.deviceID;
            var  model = _action.model;
            /* lookup device descriptor */
            DeviceDescrip ddRef;
            bool          foundOk = _descriptors.Get(model, devID, out ddRef);

            /* track the error status */
            Status retval = Status.Ok;
            /* temp for catching JSON responses */
            string response = string.Empty;
            string fileName = string.Empty;

            switch (_action.type)
            {
            case ActionType.Blink:
                /* make sure device was found in our collection */
                if (retval == Status.Ok)
                {
                    retval = (foundOk) ? Status.Ok : Status.DeviceNotFound;
                }
                /* perform http exchange */
                if (retval == Status.Ok)
                {
                    retval = _WebServerScripts.HttpGet(_hostName, ddRef.model, ddRef.deviceID, ActionType.Blink, out response);
                }
                /* parse resp */
                if (retval == Status.Ok)
                {
                    BlinkReturn jsonDeser = JsonConvert.DeserializeObject <BlinkReturn>(response);
                    retval = (Status)jsonDeser.GeneralReturn.Error;
                }
                break;

            case ActionType.SelfTest:
                /* make sure device was found in our collection */
                if (retval == Status.Ok)
                {
                    retval = (foundOk) ? Status.Ok : Status.DeviceNotFound;
                }
                /* perform http exchange */
                if (retval == Status.Ok)
                {
                    retval = _WebServerScripts.HttpGet(_hostName, ddRef.model, ddRef.deviceID, ActionType.SelfTest, out response, "", 5000);
                }
                /* parse resp */
                if (retval == Status.Ok)
                {
                    SelfTestReturn jsonDeser = JsonConvert.DeserializeObject <SelfTestReturn>(response);
                    retval = (Status)jsonDeser.GeneralReturn.Error;

                    if (retval == Status.Ok)
                    {
                        _action.selfTestResults = jsonDeser.SelfTest;
                    }
                    else
                    {
                        _action.selfTestResults = "Self-Test Failed";
                    }
                }
                break;

            case ActionType.SetDeviceName:
                /* get params for this action */
                String newName = _action.stringParam;
                /* make sure device was found in our collection */
                if (retval == Status.Ok)
                {
                    retval = (foundOk) ? Status.Ok : Status.DeviceNotFound;
                }
                /* perform http exchange */
                if (retval == Status.Ok)
                {
                    retval = _WebServerScripts.HttpGet(_hostName, ddRef.model, ddRef.deviceID, ActionType.SetDeviceName, out response, "&newname=" + newName, 2000);
                }
                /* parse resp */
                if (retval == Status.Ok)
                {
                    NameReturn jsonDeser = JsonConvert.DeserializeObject <NameReturn>(response);
                    retval = (Status)jsonDeser.GeneralReturn.Error;
                }
                break;

            case ActionType.SetID:
                /* get params for this action */
                byte newId = (byte)_action.newID;
                /* make sure device was found in our collection */
                if (retval == Status.Ok)
                {
                    retval = (foundOk) ? Status.Ok : Status.DeviceNotFound;
                }
                /* perform http exchange */
                if (retval == Status.Ok)
                {
                    retval = _WebServerScripts.HttpGet(_hostName, ddRef.model, ddRef.deviceID, ActionType.SetID, out response, "&newid=" + newId.ToString(), 2000);
                }
                /* parse resp */
                if (retval == Status.Ok)
                {
                    IDReturn jsonDeser = JsonConvert.DeserializeObject <IDReturn>(response);
                    retval = (Status)jsonDeser.GeneralReturn.Error;
                }
                /* if ID change was successful, update our local device list */
                if (retval == Status.Ok)
                {
                    _descriptors.ChangeID(ddRef, newId);
                }
                if (retval == Status.Ok)
                {
                    /* Update device cache with new deviceID, this is important when resolving/creating device ID conflicts */
                    UpdateConfigs(ddRef);
                }
                break;

            case ActionType.SetConfig:
                /* Specify the filename so it's somewhat unique */
                fileName = (ddRef.model + ": " + ddRef.deviceID + ".json").ToLower();     //ToLower Everything to make it parsable in URL

                /* get params for this action */
                string serializedData = _action.stringParam;
                /* Get content payload from action */
                byte[] dataBytes = System.Text.Encoding.UTF8.GetBytes(serializedData);

                /* If we're using GET we need to send the file up */
                if (EnableSftpTransfer)
                {
                    _rioUpdater = new RioUpdater(_hostName);
                    _rioUpdater.SendFileContents(dataBytes, _serverSearchDirectory + fileName);
                    System.Threading.Thread.Sleep(250);     //Wait a bit to make sure file got onto the RIO
                }

                /* make sure device was found in our collection */
                if (retval == Status.Ok)
                {
                    retval = (foundOk) ? Status.Ok : Status.DeviceNotFound;
                }
                /* perform http exchange */
                if (retval == Status.Ok)
                {
                    /* Decide if we're posting or getting from the _usingPost flag */
                    if (EnableSftpTransfer)
                    {
                        retval = _WebServerScripts.HttpGet(_hostName, ddRef.model, ddRef.deviceID, ActionType.SetConfig, out response, "&file=" + fileName, 5000);
                    }
                    else
                    {
                        retval = _WebServerScripts.HttpPost(_hostName, ddRef.model, ddRef.deviceID, ActionType.SetConfig, dataBytes, out response, 5000);
                    }
                }
                /* parse resp */
                if (retval == Status.Ok)
                {
                    SetConfigReturn jsonDeser = JsonConvert.DeserializeObject <SetConfigReturn>(response);
                    retval = (Status)jsonDeser.GeneralReturn.Error;
                }
                if (retval == Status.Ok)
                {
                    /* Backend should immediately update device config cache */
                    retval = UpdateConfigs(ddRef);
                }

                break;

            case ActionType.FieldUpgradeDevice:
                /* Specify file name as generic so it gets overwritten every time */
                fileName = "firmwarefile.crf";

                /* If we're using POST, we don't need to send the file up */
                if (EnableSftpTransfer)
                {
                    /* Create a RioFile to be sent to the server */
                    RioFile file = new RioFile(_action.filePath, _serverSearchDirectory + fileName);
                    /* First put the files onto the RIO */
                    _rioUpdater = new RioUpdater(_hostName);
                    _rioUpdater.SendFile(file);
                }

                /* make a new web exchange that allows for overlapped operation */
                _asyncWebExchange = new AsyncWebExchange();
                /* make sure device was found in our collection */
                if (retval == Status.Ok)
                {
                    retval = (foundOk) ? Status.Ok : Status.DeviceNotFound;
                }
                if (retval == Status.Ok)
                {
                    retval = ExecuteFieldUpgrade(ddRef, _asyncWebExchange, fileName, EnableSftpTransfer);
                }
                /* free resouces */
                _asyncWebExchange.Dispose();
                break;

            case ActionType.InstallDiagServerToRobotController:
                _rioUpdater = new RioUpdater(_hostName);
                _rioUpdater.StartUpdate();
                retval = Status.Ok;
                setStateToConnecting = true;
                break;

            case ActionType.UninstallDiagServerToRobotController:
                _rioUpdater = new RioUpdater(_hostName);
                _rioUpdater.StartRevert();
                retval = Status.Ok;
                setStateToConnecting = true;
                break;

            case ActionType.StartServer:
                _rioUpdater          = new RioUpdater(_hostName);
                retval               = _rioUpdater.StartServer();
                setStateToConnecting = true;
                break;

            case ActionType.StopServer:
                _rioUpdater          = new RioUpdater(_hostName);
                retval               = _rioUpdater.StopServer();
                setStateToConnecting = true;
                break;

            /* Unit Testing Cases */

            case ActionType.GetVersion:     //Used for Unit Testing
                VersionReturn responseClass = null;
                if (retval == Status.Ok)
                {
                    retval = _WebServerScripts.HttpGet(_hostName, "", 0, ActionType.GetVersion, out response);
                }
                if (retval == Status.Ok)
                {
                    responseClass = JsonConvert.DeserializeObject <VersionReturn>(response);
                    retval        = responseClass.Version.Equals(_action.stringParam) ? Status.Ok : Status.VersionDoesNotMatch;
                }
                break;

            case ActionType.GetNumOfDevices:
                GetDevicesReturn numDeviceReturn = null;
                if (retval == Status.Ok)
                {
                    retval = _WebServerScripts.HttpGet(_hostName, "", 0, ActionType.GetDeviceList, out response);
                }
                if (retval == Status.Ok)
                {
                    numDeviceReturn = JsonConvert.DeserializeObject <GetDevicesReturn>(response);
                    retval          = numDeviceReturn.DeviceArray.Length >= _action.param ? Status.Ok : Status.NotEnoughDevices;
                }
                break;

            case ActionType.RebootRio:
                _rioUpdater          = new RioUpdater(_hostName);
                retval               = _rioUpdater.RebootRio();
                setStateToConnecting = true;
                break;

            case ActionType.CheckProcess:
                _rioUpdater = new RioUpdater(_hostName);
                retval      = _rioUpdater.CheckProcessStarted();
                break;

            default:
                retval = Status.UnsupportedAction;
                break;
            }

            /* callback to GUI */
            _action.Error = retval;
            _action.callback(_action, retval);
            /* free it */
            _action = null;

            /* pass error to caller */
            return(retval);
        }