void FactoryDefault() {
    Config.Version = CONFIG_SCHEMA_VERSION;
    Config.VoltageLimit_counts=2068;
    Config.CurrentLimit_counts=1473;
    Config.Amps_per_Count=0.003392741;
    Config.Counts_per_Amp=294.7469417;
    Config.Counts_per_Volt = 231.782986;
    Config.Volts_per_Count = 0.0043143805193735;

    Config.SystemAcceleration = 180.0;
    Config.MaxSpeed = 180.0;

    Config.PID_MaxError = 127;
    Config.PID_Kp = 8;
    Config.PID_Kd = 8;

    Config.Counts_Per_Degree = 17.5;
    Config.Degrees_Per_Count = 0.05714286;

    Config.MyID=0xAA;

    PRESET Empty;
    Empty.Orbit.Type = 0;
    SavePreset(0, (unsigned char *) &Empty);
    SavePreset(1, (unsigned char *) &Empty);
    SavePreset(2, (unsigned char *) &Empty);
    SavePreset(3, (unsigned char *) &Empty);
    SavePreset(4, (unsigned char *) &Empty);
}
void FactoryDefault() {
    Config.Version=CONFIG_SCHEMA_VERSION;
    Config.BatteryLowVoltageLevel = 1470;
    Config.Battery12V_Q8 = 44288;
    Config.PowerResponseLimiter_Slope = 6;
    Config.PowerResponseLimiter_Intercept = 122;
    Config.Counts_per_Volt = 231.782986;
    Config.Volts_per_Count = 0.0043143805193735;

    Config.BacklightIdleTimeout = 5000;
    Config.BacklightPWM_Duty = 1;

    Config.SystemAcceleration = 180.0;
    Config.MaxSpeed = 180.0;

    Config.PID_MaxError = 32;
    Config.PID_Kp = 32;
    Config.PID_Kd = 64;

    Config.Counts_Per_Degree = 17.5;
    Config.Degrees_Per_Count = 0.05714286;

    PRESET Empty;
    Empty.Orbit.Type=0;
    SavePreset(0,(unsigned char *)&Empty);
    SavePreset(1,(unsigned char *)&Empty);
    SavePreset(2,(unsigned char *)&Empty);
    SavePreset(3,(unsigned char *)&Empty);
    SavePreset(4,(unsigned char *)&Empty);
}
AutomationPresetRef FAutomationTestPresetManager::AddNewPreset( const FText& PresetName, const TArray<FString>& SelectedTests )
{
	AutomationPresetRef NewPreset = AddNewPreset();
	NewPreset->SetPresetName(PresetName);
	NewPreset->SetEnabledTests(SelectedTests);
	SavePreset(NewPreset);
	return NewPreset;
}
void FAutomationTestPresetManager::SavePreset( const AutomationPresetRef Preset )
{
	FString PresetFileName = GetPresetFolder() / Preset->GetID().ToString() + TEXT(".uap");
	FArchive* PresetFileWriter = IFileManager::Get().CreateFileWriter(*PresetFileName);

	if (PresetFileWriter != nullptr)
	{
		SavePreset(Preset, *PresetFileWriter);

		delete PresetFileWriter;
	}
}
AutomationPresetPtr FAutomationTestPresetManager::AddNewPreset( const FText& PresetName, const TArray<FString>& SelectedTests )
{
	if ( PresetName.IsEmpty() )
	{
		return nullptr;
	}

	const FName NewNameSlug = MakeObjectNameFromDisplayLabel(PresetName.ToString(), NAME_None);

	if ( !Presets.FindByPredicate([&NewNameSlug] (const AutomationPresetPtr& Preset) { return Preset.IsValid() && Preset->GetID() == NewNameSlug; }) )
	{
		AutomationPresetRef NewPreset = MakeShareable(new FAutomationTestPreset(NewNameSlug));
		NewPreset->SetName(PresetName);
		NewPreset->SetEnabledTests(SelectedTests);

		Presets.Add(NewPreset);

		SavePreset(NewPreset);

		return NewPreset;
	}

	return nullptr;
}
void Idle() {
    unsigned int addr = 0;
    unsigned char MsgAddr = 0;
    unsigned char PresetNumber = 0;
    unsigned char *ConfigPtr;
    ConfigStruct TempConfig;
    char idx;
    MULTI temp;
    PRESET tempPset;

    if (ExtModeActive) {
        idx = 255;
        while (idx--);
        return;
    }

    if (!RX_MsgComplete) {
        idx = 255;
        while (idx--);
        return;
    }

    ChkSum = 0;
    ReadIdx = 1;
    RX_MsgComplete = 0;
    MsgAddr = MessageStream_ReadByte();
    if (MsgAddr == 0xFF) {
        TX_Idx = 0;
        TXBuffer[TX_Idx++] = '$';
        MessageStream_WriteByte(MyID);
        MessageStream_WriteByte('O');
        MessageStream_WriteByte('R');
        MessageStream_WriteByte('B');
        MessageStream_WriteByte('I');
        MessageStream_WriteByte('T');
        TXBuffer[TX_Idx++] = '#';
        idx = TX_Idx;
        TX_Idx = 0;
        TX_bCount = idx;
    } else if (MsgAddr == MyID) {
        unsigned char CmdID = MessageStream_ReadByte();
        switch (CmdID) {
            case CMD_GET_COMPLETE_STATUS:
                TX_Idx = 0;
                TXBuffer[TX_Idx++] = '$';
                MessageStream_WriteByte(CmdID);
                for (idx = 0; idx < 20; idx++) {
                    MessageStream_WriteByte(LCD_Line2[idx]);
                    ChkSum += LCD_Line2[idx];
                }
                for (idx = 0; idx < 20; idx++) {
                    MessageStream_WriteByte(LCD_Line1[idx]);
                    ChkSum += LCD_Line1[idx];
                }
                bLock_Motor_Position = 1;
                temp.dbl = Motor_Position;
                bLock_Motor_Position = 0;
                temp.dbl *= Config.Degrees_Per_Count;
                MessageStream_WriteByte(temp.ub[0]);
                MessageStream_WriteByte(temp.ub[1]);
                MessageStream_WriteByte(temp.ub[2]);
                MessageStream_WriteByte(temp.ub[3]);
                ChkSum += temp.ub[0];
                ChkSum += temp.ub[1];
                ChkSum += temp.ub[2];
                ChkSum += temp.ub[3];

                temp.dbl = 0;
                while (!(temp.dbl == Move_speedQ24)) temp.dbl = Move_speedQ24;
                temp.dbl *= INV_Conversion_Q24_500Hz;
                temp.dbl *= Config.Degrees_Per_Count;
                MessageStream_WriteByte(temp.ub[0]);
                MessageStream_WriteByte(temp.ub[1]);
                MessageStream_WriteByte(temp.ub[2]);
                MessageStream_WriteByte(temp.ub[3]);
                ChkSum += temp.ub[0];
                ChkSum += temp.ub[1];
                ChkSum += temp.ub[2];
                ChkSum += temp.ub[3];

                temp.dbl = 0;
                bLock_BatteryVoltage = 1;
                temp.dbl = BatteryVoltage;
                bLock_BatteryVoltage = 0;
                temp.dbl *= Config.Volts_per_Count;

                MessageStream_WriteByte(temp.ub[0]);
                MessageStream_WriteByte(temp.ub[1]);
                MessageStream_WriteByte(temp.ub[2]);
                MessageStream_WriteByte(temp.ub[3]);
                ChkSum += temp.ub[0];
                ChkSum += temp.ub[1];
                ChkSum += temp.ub[2];
                ChkSum += temp.ub[3];

                MessageStream_WriteByte(ChkSum);
                TXBuffer[TX_Idx++] = '#';
                idx = TX_Idx;
                TX_Idx = 0;
                TX_bCount = idx;
                break;
            case CMD_GETDISPLAY:
                TX_Idx = 0;
                TXBuffer[TX_Idx++] = '$';
                MessageStream_WriteByte(CmdID);
                for (idx = 0; idx < 20; idx++) {
                    MessageStream_WriteByte(LCD_Line2[idx]);
                    ChkSum += LCD_Line2[idx];
                }
                for (idx = 0; idx < 20; idx++) {
                    MessageStream_WriteByte(LCD_Line1[idx]);
                    ChkSum += LCD_Line1[idx];
                }
                MessageStream_WriteByte(ChkSum);
                TXBuffer[TX_Idx++] = '#';
                idx = TX_Idx;
                TX_Idx = 0;
                TX_bCount = idx;
                break;
            case CMD_UI_CLICK:
                isPendingExternalUIEvent = 0;
                PendingExternalUIEvent = USER_INPUT_CLICK;
                isPendingExternalUIEvent = 1;
                AckCmd(CmdID);
                break;
            case CMD_UI_CANCEL:
                isPendingExternalUIEvent = 0;
                PendingExternalUIEvent = USER_INPUT_CANCEL;
                isPendingExternalUIEvent = 1;
                AckCmd(CmdID);
                break;
            case CMD_UI_BACK:
                isPendingExternalUIEvent = 0;
                PendingExternalUIEvent = USER_INPUT_BACK;
                isPendingExternalUIEvent = 1;
                AckCmd(CmdID);
                break;
            case CMD_UI_INC:
                isPendingExternalUIEvent = 0;
                PendingExternalUIEvent = USER_INPUT_INC;
                isPendingExternalUIEvent = 1;
                AckCmd(CmdID);
                break;
            case CMD_UI_DEC:
                isPendingExternalUIEvent = 0;
                PendingExternalUIEvent = USER_INPUT_DEC;
                isPendingExternalUIEvent = 1;
                AckCmd(CmdID);
                break;
            case CMD_UI_MACRO:
                isPendingExternalUIEvent = 0;
                PendingExternalUIEvent = -MessageStream_ReadByte();
                isPendingExternalUIEvent = 1;
                AckCmd(CmdID);
                break;
            case CMD_GET_UI_LOC:
                TX_Idx = 0;
                TXBuffer[TX_Idx++] = '$';
                MessageStream_WriteByte(CmdID);
                MessageStream_WriteByte(UI_Location);
                MessageStream_WriteByte(UI_Location);
                TXBuffer[TX_Idx++] = '#';
                idx = TX_Idx;
                TX_Idx = 0;
                TX_bCount = idx;
                break;
            case CMD_GET_POS:
                bLock_Motor_Position = 1;
                temp.dbl = Motor_Position;
                bLock_Motor_Position = 0;
                temp.dbl *= Config.Degrees_Per_Count;
                MessageStream_WriteMULTI(temp, CmdID);
                break;
            case CMD_GET_SPEED:
                temp.dbl = 0;
                while (!(temp.dbl == Move_speedQ24)) temp.dbl = Move_speedQ24;
                temp.dbl *= INV_Conversion_Q24_500Hz;
                temp.dbl *= Config.Degrees_Per_Count;
                MessageStream_WriteMULTI(temp, CmdID);
                break;
            case CMD_GET_BATTERY:
                temp.dbl = 0;
                bLock_BatteryVoltage = 1;
                temp.dbl = BatteryVoltage;
                bLock_BatteryVoltage = 0;
                temp.dbl *= Config.Volts_per_Count;
                MessageStream_WriteMULTI(temp, CmdID);
                break;
            case CMD_GET_PRESET:
                PresetNumber = MessageStream_ReadByte();

                if (PresetNumber > 5) {
                    NackCmd(CmdID, 1);
                    break;
                }
                if (PresetNumber == 0) {
                    NackCmd(CmdID, 1);
                    break;
                }

                LoadPreset(PresetNumber, (unsigned char *) &tempPset);
                TX_Idx = 0;
                TXBuffer[TX_Idx++] = '$';
                MessageStream_WriteByte(CmdID);
                for (idx = 0; idx<sizeof (tempPset); idx++) {
                    MessageStream_WriteByte(tempPset.ub[idx]);
                    ChkSum += tempPset.ub[idx];
                }
                MessageStream_WriteByte(ChkSum);
                TXBuffer[TX_Idx++] = '#';
                idx = TX_Idx;
                TX_Idx = 0;
                TX_bCount = idx;
                break;
            case CMD_SET_PRESET:
                PresetNumber = MessageStream_ReadByte();
                if (PresetNumber > 5) {
                    NackCmd(CmdID, 1);
                    break;
                }
                if (PresetNumber == 0) {
                    NackCmd(CmdID, 1);
                    break;
                }

                ChkSum += PresetNumber;

                for (idx = 0; idx < 120; idx++) {
                    tempPset.ub[idx] = MessageStream_ReadByte();
                    ChkSum += tempPset.ub[idx];
                }

                if (ChkSum == MessageStream_ReadByte()) {
                    SavePreset(PresetNumber, (unsigned char *) &tempPset);
                    AckCmd(CmdID);
                } else {
                    NackCmd(CmdID, ChkSum);
                }
                break;
            case CMD_GET_CONFIG:
                ConfigPtr = (unsigned char *) &Config;
                TX_Idx = 0;
                TXBuffer[TX_Idx++] = '$';
                MessageStream_WriteByte(CmdID);
                for (idx = 0; idx<sizeof (Config); idx++) {
                    MessageStream_WriteByte((*ConfigPtr));
                    ChkSum += (*ConfigPtr);
                    ConfigPtr++;
                }
                MessageStream_WriteByte(ChkSum);
                TXBuffer[TX_Idx++] = '#';
                idx = TX_Idx;
                TX_Idx = 0;
                TX_bCount = idx;
                break;
            case CMD_SET_CONFIG:
                ConfigPtr = (unsigned char *) &TempConfig;
                for (idx = 0; idx < sizeof (Config); idx++) {
                    (*ConfigPtr) = MessageStream_ReadByte();
                    ChkSum += (*ConfigPtr);
                    ConfigPtr++;
                }

                if (ChkSum == MessageStream_ReadByte()) {
                    addr=0;
                    EEprom_write(&addr, (unsigned char *) &TempConfig, sizeof (TempConfig));
                    AckCmd(CmdID);
                } else {
                    NackCmd(CmdID, ChkSum);
                }
                break;
            default:
                NackCmd(CmdID, 0xFF);
        }
    }
    return;
}
signed char CreateOrbitProgram() {
    UI_Location = UI_LOC_ORBITSETUP;
    signed char ret;

    const char *DIRECTION_1 = "CLOCKWISE\0";
    const char *DIRECTION_0 = "COUNTER CLOCKWISE\0";
    const char*DirectionMenu[2];
    DirectionMenu[0] = DIRECTION_0;
    DirectionMenu[1] = DIRECTION_1;

    const char *SpeedMODE_0 = "MANUAL\0";
    const char *SpeedMODE_1 = "PER ORBIT\0";
    const char *SpeedMODE_2 = "FOR ALL ORBITS\0";
    const char*SpeedModeMenu[3];
    SpeedModeMenu[0] = SpeedMODE_0;
    SpeedModeMenu[1] = SpeedMODE_1;
    SpeedModeMenu[2] = SpeedMODE_2;

    const char *ENDMODE_0 = "BY ORBIT COUNT\0";
    const char *ENDMODE_1 = "BY TOTAL TIME\0";
    const char *ENDMODE_2 = "NEVER ENDING\0";
    const char*EndModeMenu[3];
    EndModeMenu[0] = ENDMODE_0;
    EndModeMenu[1] = ENDMODE_1;
    EndModeMenu[2] = ENDMODE_2;

    const char *COMMAND_0 = "RUN PROGRAM\0";
    const char *COMMAND_1 = "SAVE PRESET\0";
    const char*CommandMenu[2];
    CommandMenu[0] = COMMAND_0;
    CommandMenu[1] = COMMAND_1;

labelOrigin:
    LCD_ClearDisplay();
    LCD_PrintString("MOVE TO START...\0");
    LCD_SetPosition(1, 0);
    LCD_PrintString("THEN CLICK.\0");
    bFollowMode = 1;
    ret = GetClick();
    bFollowMode = 0;
    if (ret < 0) return ret;    
    CurrentOrbitProgram.Origin_deg = (unsigned int) GetCurrentAngle();

labelDirection:
    ret = DisplayChoices(DirectionMenu, 1, "ROTATION DIRECTION:\0", CurrentOrbitProgram.IsClockWise);
    if (ret == -1) goto labelOrigin;
    if (ret == -2) return (-2);
    if (ret < 0) return ret;
    CurrentOrbitProgram.IsClockWise = ret;

labelEndMode:
    ret = DisplayChoices(EndModeMenu, 2, "PROGRAM END MODE:\0", CurrentOrbitProgram.EndMode);
    if (ret == -1) goto labelDirection;
    if (ret == -2) return (-2);
    if (ret < 0) return ret;
    CurrentOrbitProgram.EndMode = ret;
    switch (CurrentOrbitProgram.EndMode) {
        case 0: //End based on Cycle Count.
            labelCycleCount :
            if (CurrentOrbitProgram.CycleCount_rev < 1) CurrentOrbitProgram.CycleCount_rev = 1;
            if (CurrentOrbitProgram.CycleCount_rev > 999) CurrentOrbitProgram.CycleCount_rev = 999;

            ret = GetFloat("ORBIT LIMIT:\0", "", &CurrentOrbitProgram.CycleCount_rev, 1, 999, 1.0);
            if (ret == -1) goto labelEndMode;
            if (ret == -2) return (-2);
            if (ret < 0) return ret;

labelSpeedMode:
            if (CurrentOrbitProgram.CycleCount_rev > 1) ret = DisplayChoices(SpeedModeMenu, 2, "ORBIT SPEED:\0", CurrentOrbitProgram.SpeedMode);
            else ret = DisplayChoices(SpeedModeMenu, 1, "ORBIT SPEED:\0", CurrentOrbitProgram.SpeedMode);
            if (ret == -1) goto labelCycleCount;
            if (ret == -2) return (-2);
            if (ret < 0) return ret;
            switch (ret) {
                case 0:

                    if (CurrentOrbitProgram.Speed_deg_sec < 0.01) CurrentOrbitProgram.Speed_deg_sec = 0.01;
                    ret = GetFloat("SPEED\0", "\xDF/Sec\0", &CurrentOrbitProgram.Speed_deg_sec, 0.01, 90, 0.01);
                    if (ret == -1) goto labelSpeedMode;
                    if (ret == -2) return (-2);
                    break;
                case 1:
                    if (CurrentOrbitProgram.CycleTime_sec < 4) CurrentOrbitProgram.CycleTime_sec = 4;
                    ret = GetTime("TIME PER ORBIT\0", &CurrentOrbitProgram.CycleTime_sec, 4, 86400, 0b1110);
                    if (ret == -1) goto labelSpeedMode;
                    if (ret == -2) return (-2);
                    CurrentOrbitProgram.Speed_deg_sec = 360 / CurrentOrbitProgram.CycleTime_sec;
                    break;
                case 2:
                    if (CurrentOrbitProgram.CycleTime_sec < (4 * CurrentOrbitProgram.CycleCount_rev)) CurrentOrbitProgram.CycleTime_sec = 4 * CurrentOrbitProgram.CycleCount_rev;
                    ret = GetTime("TIME FOR ALL ORBITS\0", &CurrentOrbitProgram.CycleTime_sec, 4 * CurrentOrbitProgram.CycleCount_rev, 86400, 0b1110);
                    if (ret == -1) goto labelSpeedMode;
                    if (ret == -2) return (-2);
                    CurrentOrbitProgram.Speed_deg_sec = (360 * CurrentOrbitProgram.CycleCount_rev) / CurrentOrbitProgram.CycleTime_sec;
                    break;
            }
            break;
        case 1://End based on runtime.
            labelProgramRuntime :

                    ret = GetTime("TOTAL RUNTIME:\0", &CurrentOrbitProgram.ProgramRunTime_sec, 1, 86400, 0b1110);
            if (ret == -1) goto labelEndMode;
            if (ret == -2) return (-2);
            if (ret < 0) return ret;
labelSpeedMode2:

            ret = DisplayChoices(SpeedModeMenu, 1, "ORBIT SPEED:\0", CurrentOrbitProgram.SpeedMode);
            if (ret == -1) goto labelProgramRuntime;
            if (ret == -2) return (-2);
            if (ret < 0) return ret;
            switch (ret) {
                case 0:
                    if (CurrentOrbitProgram.Speed_deg_sec < 0.01) CurrentOrbitProgram.Speed_deg_sec = 0.01;
                    ret = GetFloat("SPEED\0", "\xDF/Sec\0", &CurrentOrbitProgram.Speed_deg_sec, 0.01, 90, 0.01);
                    if (ret == -1) goto labelSpeedMode2;
                    if (ret == -2) return (-2);
                    break;
                case 1:
                    if (CurrentOrbitProgram.CycleTime_sec < 4) CurrentOrbitProgram.CycleTime_sec = 4;
                    ret = GetTime("TIME PER ORBIT:\0", &CurrentOrbitProgram.CycleTime_sec, 4, 86400, 0b1110);
                    if (ret == -1) goto labelSpeedMode2;
                    if (ret == -2) return (-2);
                    CurrentOrbitProgram.Speed_deg_sec = 360 / CurrentOrbitProgram.CycleTime_sec;
                    break;
            }
            CurrentOrbitProgram.CycleCount_rev = (CurrentOrbitProgram.Speed_deg_sec * CurrentOrbitProgram.ProgramRunTime_sec) / 360;
            break;
        case 2: //NO END AT ALL
            labelSpeedMode3 :
                    ret = DisplayChoices(SpeedModeMenu, 1, "ORBIT SPEED:\0", CurrentOrbitProgram.SpeedMode);
            if (ret == -1) goto labelEndMode;
            if (ret == -2) return (-2);
            if (ret < 0) return ret;
            switch (ret) {
                case 0:
                    if (CurrentOrbitProgram.Speed_deg_sec < 0.01) CurrentOrbitProgram.Speed_deg_sec = 0.01;
                    ret = GetFloat("SPEED\0", "\xDF/Sec\0", &CurrentOrbitProgram.Speed_deg_sec, 0.01, 90, 0.01);
                    if (ret == -1) goto labelSpeedMode3;
                    if (ret == -2) return (-2);
                    if (ret < 0) return ret;
                    break;
                case 1:
                    if (CurrentOrbitProgram.CycleTime_sec < 4) CurrentOrbitProgram.CycleTime_sec = 4;
                    ret = GetTime("TIME PER ORBIT:\0", &CurrentOrbitProgram.CycleTime_sec, 4, 86400, 0b1110);
                    if (ret == -1) goto labelSpeedMode3;
                    if (ret == -2) return (-2);
                    if (ret < 0) return ret;
                    CurrentOrbitProgram.Speed_deg_sec = 360 / CurrentOrbitProgram.CycleTime_sec;
                    break;
            }
            break;

    }

labelActions:
    ret = DisplayChoices(CommandMenu, 1, "ACTION:\0", 0);
    if (ret == -1) goto labelCycleCount;
    if (ret == -2) return (-2);
    if (ret < 0) return ret;
    if (ret == 0) {
        return RunOrbitProgram();
    }

    ret = GetPresetNumber();
    if (ret == -1) goto labelActions;
    if (ret == -2) return (-2);
    if (ret < 0) return ret;

    CurrentOrbitProgram.Type = 2;
    SavePreset(ret, (unsigned char *) &CurrentOrbitProgram);
    goto labelActions;
}