//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "ThreeDLineScanFrequencyStudy_Form.h"
#include "math.h"
#include "MultiLanguage.h"
#include "Message_Form.h"

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "TntExtCtrls"
#pragma resource "*.dfm"
TThreeDLineScanFrequencyStudyForm *ThreeDLineScanFrequencyStudyForm;
//---------------------------------------------------------------------------
__fastcall TThreeDLineScanFrequencyStudyForm::TThreeDLineScanFrequencyStudyForm(TComponent* Owner)
  : TTntForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TThreeDLineScanFrequencyStudyForm::DiskMinSpeedEditKeyPress(
      TObject *Sender, char &Key)
{
  Key = NULL;
}
//---------------------------------------------------------------------------

void __fastcall TThreeDLineScanFrequencyStudyForm::DiskMinSpeedEditClick(
      TObject *Sender)
{
  TTntEdit *theEdit = (TTntEdit *) Sender;

  int currentSpeed = StrToInt(theEdit->Text);
  currentSpeed = MachineSetupData.ServoMotorData[0].BaseSpeed * 100 / currentSpeed;

	TMotorSpeedSelectionForm *motorSpeedSelectionForm = new TMotorSpeedSelectionForm(this);
  motorSpeedSelectionForm->SetSpeed(MachineSetupData.ServoMotorData[0].BaseSpeed, currentSpeed);
  if (motorSpeedSelectionForm->ShowModal() == mrOk)
  {
    theEdit->Text = IntToStr(MachineSetupData.ServoMotorData[0].BaseSpeed * 100 / motorSpeedSelectionForm->GetSelectedSpeed());
  }
  delete motorSpeedSelectionForm;  
}
//---------------------------------------------------------------------------

void __fastcall TThreeDLineScanFrequencyStudyForm::RefreshDiskSpeed(int DiskPercentage, float Disk1ExpectedRPM, float Disk1RealRPM, float Disk2ExpectedRPM, float Disk2RealRPM)
{
  DiskSpeedPercentageLabel->Caption = IntToStr(DiskPercentage) + "%";

  WCHAR TempString[100];

 	wsprintfW(TempString, L"%S", FloatToStrF(Disk1ExpectedRPM, ffFixed, 10, 3));
  Disk1ExpectedRPMLabel->Caption = TempString;

  wsprintfW(TempString, L"%S", FloatToStrF(Disk2ExpectedRPM, ffFixed, 10, 3));
  Disk2ExpectedRPMLabel->Caption = TempString;

  wsprintfW(TempString, L"%S", FloatToStrF(Disk1RealRPM, ffFixed, 10, 3));
  Disk1RealRPMLabel->Caption = TempString;

  wsprintfW(TempString, L"%S", FloatToStrF(Disk2RealRPM, ffFixed, 10, 3));
  Disk2RealRPMLabel->Caption = TempString;
}
//---------------------------------------------------------------------------

void __fastcall TThreeDLineScanFrequencyStudyForm::FormCreate(
      TObject *Sender)
{
  HelpLabel1->Caption = THREED_LSF_STUDY_FORM_TEXT_01;
  HelpLabel2->Caption = THREED_LSF_STUDY_FORM_TEXT_02;
  HelpLabel3->Caption = THREED_LSF_STUDY_FORM_TEXT_03;
  ReadyPanel->Caption = THREED_LSF_STUDY_FORM_TEXT_04;
  
  Panel2->DoubleBuffered = true;
  Panel4->DoubleBuffered = true;
  
  bThreeDLSFStudySW = false;
  bCaptureSW = false;
  bSetCaptureCommandSW = false;
  bRPMIncreaseWaitingSW = false;
  bMotorStop = false;
  RefreshDiskSpeed(0, 0, 0, 0, 0);
  RefreshLSFValue(0, 0, 0, 0, 0);
  RefreshComponentStatus(false);
  MotorSpeedControlTimer->Enabled = false;

  SetBlankImage(SD1ThreeDImage, 0);
  SetBlankImage(SD2ThreeDImage, 0);
  SetBlankImage(LSFResultImage, 1);
  SetBlankImage(SD1ResultImage, 1);
  SetBlankImage(SD2ResultImage, 1);
  SetBlankImage(ModelingImage, 1);

  SetResultTable(true);
  globalLSFMaxValue = 0;
  
  SD1ParamsLabel->Caption = "";
  SD2ParamsLabel->Caption = "";

  SetBlankImage(SD1BestCaseThreeDImage, 0);
  SetBlankImage(SD2BestCaseThreeDImage, 0);
  
  SD1BestParamsLabel->Caption = "";
  SD2BestParamsLabel->Caption = "";

  SD1MatchingRateLabel->Caption = "SD1 Best Matching Rate : NULL";
  SD2MatchingRateLabel->Caption = "SD2 Best Matching Rate : NULL";

  SD1MaxMatchingValue = 0;
  SD2MaxMatchingValue = 0;

  SD1BestMatchingLSF = 0;
  SD2BestMatchingLSF = 0;

  MortorStatusLabel->Caption = "Standby";
  CameraStatusLabel->Caption = "Standby";

  FirstThreeDCameraIndex = SD1_3D_FRONT_FACE_CAMERA_INDEX - 1;
  SecondThreeDCameraIndex = SD2_3D_FRONT_FACE_CAMERA_INDEX - 1;

  AnsiString fileName = ProgramPath.Env + "\\LSFStudyResultTable.lsf";
  if(!ReadDiskLSFTable(fileName))
  {
    memset(SD1LineScanFrequencyTable, 0, sizeof(int) * MAX_MOTOR_SPEED);
    memset(SD2LineScanFrequencyTable, 0, sizeof(int) * MAX_MOTOR_SPEED);
  }

  memset(LSFModelingTable, 0, sizeof(int) * MAX_MOTOR_SPEED);

  MakeDefaultTable(Disk1LSFTableStringGrid);
  MakeDefaultTable(Disk2LSFTableStringGrid);

  SetTableValue(Disk1LSFTableStringGrid, SD1LineScanFrequencyTable, true);
  SetTableValue(Disk2LSFTableStringGrid, SD2LineScanFrequencyTable, true);

  if(RANGER_3D_BASE_SHUTTER_SPEED > 2000)
  {
    LSFShfitEdit->Text = "20";
  }
  else
  {
    LSFShfitEdit->Text = "4";
  }

  globalDefaultLSFRange = RANGER_3D_BASE_SHUTTER_SPEED / 10;

  PageControl1->ActivePageIndex = 0;

  if(PCProgramOption.ApplyLSFTableSW)
  {
    SystemParamsCheckBox->Checked = true;
  }
  else
  {
    SystemParamsCheckBox->Checked = false;
  }
}
//---------------------------------------------------------------------------

void __fastcall TThreeDLineScanFrequencyStudyForm::RefreshComponentStatus(bool machineActive)
{
  if(machineActive == false)
  {
    MachineRunBtn->Enabled = true;
    MachineStopBtn->Enabled = false;
    CloseButton->Enabled = true;
    DiskMinSpeedEdit->Enabled = true;
    DiskMaxSpeedEdit->Enabled = true;
    TabletLengthEdit->Enabled = true;
    LSFRangeEdit->Enabled = true;
    LSFShfitEdit->Enabled = true;
    LoadResultButton->Enabled = true;
    ResultStringGrid->Enabled = true;
    StudyResultTestCheckBox->Enabled = true;
  }
  else
  {
    MachineRunBtn->Enabled = false;
    MachineStopBtn->Enabled = true;
    CloseButton->Enabled = false;
    DiskMinSpeedEdit->Enabled = false;
    DiskMaxSpeedEdit->Enabled = false;
    TabletLengthEdit->Enabled = false;
    LSFRangeEdit->Enabled = false;
    LSFShfitEdit->Enabled = false;
    LoadResultButton->Enabled = false;
    ResultStringGrid->Enabled = false;
    StudyResultTestCheckBox->Enabled = false;
  }

  ApplyLSFModelBtn->Enabled = false;
}
//---------------------------------------------------------------------------
void __fastcall TThreeDLineScanFrequencyStudyForm::MachineRunBtnClick(
      TObject *Sender)
{
  int minSpeed = StrToInt(DiskMinSpeedEdit->Text);
  int maxSpeed = StrToInt(DiskMaxSpeedEdit->Text);

  if(minSpeed > maxSpeed)
  {
    ShowMessage("Invalid value[MIN > MAX]");
    return;
  }

  if(minSpeed <= MIN_VALID_DISK_PERCENTAGE)
  {
    ShowMessage("Invalid value[MIN <= 20%]");
    return;
  }

  if(maxSpeed > MAX_VALID_DISK_PERCENTAGE)
  {
    ShowMessage("Invalid value[MAX > 200%]");
    return;
  }

  if(StudyResultTestCheckBox->Checked)
  {
    int maxValue = 0;
    for(int m = 0; m < MAX_MOTOR_SPEED; m++)
    {
      if(maxValue < SD1LineScanFrequencyTable[m])
      {
        maxValue = SD1LineScanFrequencyTable[m];
      }

      if(maxValue < SD2LineScanFrequencyTable[m])
      {
        maxValue = SD2LineScanFrequencyTable[m];
      }
    }

    if(maxValue == 0)
    {
      ShowMessage("Not exist study result");
      return;
    }
  }

  bRPMIncreaseWaitingSW = false;
  bMotorStop = false;
  currentSpeed = minSpeed;
  firstSpeed = minSpeed;

  RefreshComponentStatus(true);

  ReadyPanel->Visible = true;
  ReadyPanel->BringToFront();
  ReadyPanel->Refresh();
  
  SetTableValue(Disk1LSFTableStringGrid, SD1LineScanFrequencyTable, true);
  SetTableValue(Disk2LSFTableStringGrid, SD2LineScanFrequencyTable, true);

  this->Refresh();

  cleaningWaitingSW = true;
  Machine.MachineCleaning();

  while(1)
  {
    Machine.ReadPortStatus();

    Application->ProcessMessages();

    if(Machine.Status.OperationState == MACHINE_STATE_STOP ||
        Machine.Status.OperationState == MACHINE_STATE_CLEANING)
    {
      cleaningWaitingSW = false;
      break;
    }

    if(bMotorStop)
    {
      MotorSpeedControlTimer->Enabled = false;

      RefreshDiskSpeed(0, 0, 0, 0, 0);
      RefreshLSFValue(0, 0, 0, 0, 0);
      RefreshComponentStatus(false);

      ReadyPanel->Visible = false;
      ReadyPanel->Refresh();

      return;
    }
  }

  ReadyPanel->Visible = false;
  ReadyPanel->Refresh();

  SetBlankImage(SD1ThreeDImage, 0);
  SetBlankImage(SD2ThreeDImage, 0);
  SetBlankImage(LSFResultImage, 1);
  SetBlankImage(SD1ResultImage, 1);
  SetBlankImage(SD2ResultImage, 1);
  SetBlankImage(ModelingImage, 1);

  SetResultTable(true);
  globalLSFMaxValue = 0;

  SD1ParamsLabel->Caption = "";
  SD2ParamsLabel->Caption = "";

  SetBlankImage(SD1BestCaseThreeDImage, 0);
  SetBlankImage(SD2BestCaseThreeDImage, 0);
  
  SD1BestParamsLabel->Caption = "";
  SD2BestParamsLabel->Caption = "";

  SD1MatchingRateLabel->Caption = "SD1 Best Matching Rate : NULL";
  SD2MatchingRateLabel->Caption = "SD2 Best Matching Rate : NULL";

  SD1MaxMatchingValue = 0;
  SD2MaxMatchingValue = 0;

  SD1BestMatchingLSF = 0;
  SD2BestMatchingLSF = 0;

  MortorStatusLabel->Caption = "Standby";
  CameraStatusLabel->Caption = "Standby";

  RefreshCameraInfo(FirstThreeDCameraIndex);
  RefreshCameraInfo(SecondThreeDCameraIndex);
  
  bCaptureSW = false;
  bSetCaptureCommandSW = false;
  bThreeDLSFStudySW = false;

  Disk1prevRealRPM = 0;
  Disk2prevRealRPM = 0;

  int targetSpeed;
  float Disk1NextExpectedRPM, Disk2NextExpectedRPM;

  targetSpeed = MachineSetupData.ServoMotorData[0].BaseSpeed * 100 / currentSpeed;
  Disk1NextExpectedRPM = MachineSetupData.ServoMotorData[0].BaseSpeed * 54.0 / (float)targetSpeed;
  Disk2NextExpectedRPM = MachineSetupData.ServoMotorData[1].BaseSpeed * 54.0 / (float)targetSpeed;

  Disk1IncreaseRPM = (Disk1NextExpectedRPM) * 0.5;
  Disk2IncreaseRPM = (Disk2NextExpectedRPM) * 0.5;

  checkTimeForInActive = Now().Val;

  RunMotor();

  MotorSpeedControlTimer->Enabled = true;
}
//---------------------------------------------------------------------------

void __fastcall TThreeDLineScanFrequencyStudyForm::MachineStopBtnClick(
      TObject *Sender)
{
  Comm_Request(COMM_HCB, CMD_MACHINE_CAPTURE_STOP);
  MachineStopBtn->Enabled = false;
  
  if(!cleaningWaitingSW)
  {
    bRPMIncreaseWaitingSW = false;
    bMotorStop = true;

    int decTime;
    decTime = MachineSetupData.ServoMotorData[0].DecTime;
    Machine.ServoMotorStop(0, decTime);
    decTime = MachineSetupData.ServoMotorData[1].DecTime;
    Machine.ServoMotorStop(1, decTime);

    MortorStatusLabel->Caption = "Decrease";
    CameraStatusLabel->Caption = "Standby";

    //ShowMessage(THREED_LSF_STUDY_FORM_TEXT_05);
  }
  else
  {
    bRPMIncreaseWaitingSW = false;
    bMotorStop = true;
  }
}
//--------------------------------------------------------------------------- 

void __fastcall TThreeDLineScanFrequencyStudyForm::MotorSpeedControlTimerTimer(
      TObject *Sender)
{
  bool timerEnabled = MotorSpeedControlTimer->Enabled;
  bool InActiveSW = false;

  float Disk1NextExpectedRPM, Disk2NextExpectedRPM;
  float Disk1ExpectedRPM, Disk2ExpectedRPM;
  float Disk1RealRPM, Disk2RealRPM;

  int targetSpeed = MachineSetupData.ServoMotorData[0].BaseSpeed * 100 / currentSpeed;
  int maxSpeed = StrToInt(DiskMaxSpeedEdit->Text);
  bool motorSpeedStableSW = false;

  if(bMotorStop == false)
  {
    Disk1ExpectedRPM = MachineSetupData.ServoMotorData[0].BaseSpeed * 54.0 / (float)targetSpeed;
    Disk2ExpectedRPM = MachineSetupData.ServoMotorData[1].BaseSpeed * 54.0 / (float)targetSpeed;

    Disk1RealRPM = Machine.GetServoMotorRPM(0);
    Disk2RealRPM = Machine.GetServoMotorRPM(1);

    RefreshDiskSpeed(currentSpeed, Disk1ExpectedRPM, Disk1RealRPM, Disk2ExpectedRPM, Disk2RealRPM);

    double currentCheckTime = Now().Val;
    
    if((currentCheckTime - checkTimeForInActive) * 24 * 3600 >= 20)
    {
      InActiveSW = true;
    }

    if(currentSpeed == firstSpeed)
    {
      if((Disk1RealRPM >= (Disk1prevRealRPM + Disk1IncreaseRPM) && Disk2RealRPM >= (Disk2prevRealRPM + Disk2IncreaseRPM)))
      {
        motorSpeedStableSW = true;
      }
    }
    else
    {
      if((Disk1RealRPM >= (Disk1prevRealRPM + Disk1IncreaseRPM) && Disk2RealRPM >= (Disk2prevRealRPM + Disk2IncreaseRPM)))
      {
        motorSpeedStableSW = true;
      }
      else if((currentCheckTime - motorSpeedIncreaseTime) * 24 * 3600 >= 3)
      {
        motorSpeedStableSW = true;
      }
    }

    if(motorSpeedStableSW || InActiveSW)
    {
      if(bRPMIncreaseWaitingSW == false)
      {
        checkTimeForMaximumIncrease = Now().Val;
        bRPMIncreaseWaitingSW = true;
      }

      if(bRPMIncreaseWaitingSW || InActiveSW)
      {
        if(((currentCheckTime - checkTimeForMaximumIncrease) * 24 * 3600 >= 1) || InActiveSW) // 3 / 4만큼의 속도 증가 후 최대 속도까지 올라가는데 1초로 가정
        {
          if(bCaptureSW == false && InActiveSW == false && bThreeDLSFStudySW == false)
          {
            bCaptureSW = true;
            bSetCaptureCommandSW = false;

            if(currentSpeed == firstSpeed)
            {
              int SettingSpeed;
              
              SettingSpeed = MachineSetupData.ServoMotorData[0].BaseSpeed * 100 / currentSpeed;
              SD1StandardLineScanFrq = RANGER_3D_BASE_SHUTTER_SPEED * SettingSpeed / MachineSetupData.ServoMotorData[0].BaseSpeed;

              SettingSpeed = MachineSetupData.ServoMotorData[1].BaseSpeed * 100 / currentSpeed;
              SD2StandardLineScanFrq = RANGER_3D_BASE_SHUTTER_SPEED2 * SettingSpeed / MachineSetupData.ServoMotorData[1].BaseSpeed;

              int LSFRange = StrToInt(LSFRangeEdit->Text);

              SD1MinLSF = max(0, SD1StandardLineScanFrq - LSFRange);
              SD2MinLSF = max(0, SD2StandardLineScanFrq - LSFRange);

              SD1MaxLSF = SD1StandardLineScanFrq + LSFRange;
              SD2MaxLSF = SD2StandardLineScanFrq + LSFRange;

              SD1LSFPosition = SD1MinLSF;
              SD2LSFPosition = SD2MinLSF;

              SD1MaxMatchingValue = 0;
              SD2MaxMatchingValue = 0;

              SD1BestMatchingLSF = 0;
              SD2BestMatchingLSF = 0;
            }
            else
            {
              // 학습결과 적용
              int prevDiskSpeed;
              int SettingSpeed;

              prevDiskSpeed = MachineSetupData.ServoMotorData[0].BaseSpeed * 100 / (currentSpeed - 1);
              SD1StandardLineScanFrq = SD1LineScanFrequencyTable[prevDiskSpeed];

              prevDiskSpeed = MachineSetupData.ServoMotorData[1].BaseSpeed * 100 / (currentSpeed - 1);
              SD2StandardLineScanFrq = SD2LineScanFrequencyTable[prevDiskSpeed];

              int LSFRange = StrToInt(LSFRangeEdit->Text);

              if(SD1StandardLineScanFrq)
              {
                SD1MinLSF = max(50, SD1StandardLineScanFrq - globalDefaultLSFRange);
                SD1MaxLSF = SD1StandardLineScanFrq + globalDefaultLSFRange;
              }
              else
              {
                SettingSpeed = MachineSetupData.ServoMotorData[0].BaseSpeed * 100 / currentSpeed;
                SD1StandardLineScanFrq = RANGER_3D_BASE_SHUTTER_SPEED * SettingSpeed / MachineSetupData.ServoMotorData[0].BaseSpeed;
              
                SD1MinLSF = max(50, SD1StandardLineScanFrq - LSFRange);
                SD1MaxLSF = SD1StandardLineScanFrq + LSFRange;
              }

              if(SD2StandardLineScanFrq)
              {
                SD2MinLSF = max(50, SD2StandardLineScanFrq - globalDefaultLSFRange);
                SD2MaxLSF = SD2StandardLineScanFrq + globalDefaultLSFRange;
              }
              else
              {
                SettingSpeed = MachineSetupData.ServoMotorData[1].BaseSpeed * 100 / currentSpeed;
                SD2StandardLineScanFrq = RANGER_3D_BASE_SHUTTER_SPEED2 * SettingSpeed / MachineSetupData.ServoMotorData[1].BaseSpeed;
              
                SD2MinLSF = max(50, SD2StandardLineScanFrq - LSFRange);
                SD2MaxLSF = SD2StandardLineScanFrq + LSFRange;
              }

              SD1LSFPosition = SD1MinLSF;
              SD2LSFPosition = SD2MinLSF;

              SD1MaxMatchingValue = 0;
              SD2MaxMatchingValue = 0;

              SD1BestMatchingLSF = 0;
              SD2BestMatchingLSF = 0;
            }

            if(StudyResultTestCheckBox->Checked)
            {
              int SettingSpeed;
              SettingSpeed = MachineSetupData.ServoMotorData[0].BaseSpeed * 100 / currentSpeed;
              SD1StandardLineScanFrq = SD1LineScanFrequencyTable[SettingSpeed];

              SettingSpeed = MachineSetupData.ServoMotorData[1].BaseSpeed * 100 / currentSpeed;
              SD2StandardLineScanFrq = SD2LineScanFrequencyTable[SettingSpeed];

              SD1MinLSF = SD1StandardLineScanFrq;
              SD1MaxLSF = SD1StandardLineScanFrq;

              SD2MinLSF = SD2StandardLineScanFrq;
              SD2MaxLSF = SD2StandardLineScanFrq;

              SD1LSFPosition = SD1MinLSF;
              SD2LSFPosition = SD2MinLSF;

              SD1MaxMatchingValue = 0;
              SD2MaxMatchingValue = 0;

              SD1BestMatchingLSF = 0;
              SD2BestMatchingLSF = 0;

              if(SD1StandardLineScanFrq == 0 || SD2StandardLineScanFrq == 0)
              {
                // 학습 값이 존재하지 않으면 해당 rpm은 skip
                bCaptureSW = false;
                bThreeDLSFStudySW = true;
              }
            }
          }

          if(InActiveSW || bThreeDLSFStudySW)
          {
            if(StudyResultTestCheckBox->Checked == false)
            {
              if(bThreeDLSFStudySW)
              {
                // set study result
                targetSpeed = MachineSetupData.ServoMotorData[0].BaseSpeed * 100 / currentSpeed;
                if(SD1MaxMatchingValue)
                {
                  SD1LineScanFrequencyTable[targetSpeed] = SD1BestMatchingLSF;
                }
                else
                {
                  SD1LineScanFrequencyTable[targetSpeed] = 0;
                }

                if(SD2MaxMatchingValue)
                {
                  SD2LineScanFrequencyTable[targetSpeed] = SD2BestMatchingLSF;
                }
                else
                {
                  SD2LineScanFrequencyTable[targetSpeed] = 0;
                }

                SetTableValue(Disk1LSFTableStringGrid, SD1LineScanFrequencyTable, false);
                SetTableValue(Disk2LSFTableStringGrid, SD2LineScanFrequencyTable, false);
              }
              else if(InActiveSW)
              {
                targetSpeed = MachineSetupData.ServoMotorData[0].BaseSpeed * 100 / currentSpeed;

                SD1LineScanFrequencyTable[targetSpeed] = 0;
                SD2LineScanFrequencyTable[targetSpeed] = 0;

                SetTableValue(Disk1LSFTableStringGrid, SD1LineScanFrequencyTable, false);
                SetTableValue(Disk2LSFTableStringGrid, SD2LineScanFrequencyTable, false);
              }
            }
            else
            {
              SetTableValue(Disk1LSFTableStringGrid, SD1LineScanFrequencyTable, false);
              SetTableValue(Disk2LSFTableStringGrid, SD2LineScanFrequencyTable, false);
            }

            bSetCaptureCommandSW = false;
            bCaptureSW = false;
            bThreeDLSFStudySW = false;
            
            Disk1prevRealRPM = Disk1RealRPM;
            Disk2prevRealRPM = Disk2RealRPM;

            targetSpeed = MachineSetupData.ServoMotorData[0].BaseSpeed * 100 / (currentSpeed + 1);
            Disk1NextExpectedRPM = MachineSetupData.ServoMotorData[0].BaseSpeed * 54.0 / (float)targetSpeed;
            Disk2NextExpectedRPM = MachineSetupData.ServoMotorData[1].BaseSpeed * 54.0 / (float)targetSpeed;

            Disk1IncreaseRPM = (Disk1NextExpectedRPM - Disk1ExpectedRPM) * 0.5;
            Disk2IncreaseRPM = (Disk2NextExpectedRPM - Disk2ExpectedRPM) * 0.5;

            currentSpeed++;

            bRPMIncreaseWaitingSW = false;

            if(currentSpeed <= maxSpeed)
            {
              AnsiString fileName = ProgramPath.Env + "\\LSFStudyResultTable.lsf";
              WriteDiskLSFTable(fileName);
      
              checkTimeForInActive = Now().Val;

              RunMotor();
            }
            else
            {
              bMotorStop = true;

              int decTime;
              decTime = MachineSetupData.ServoMotorData[0].DecTime;
              Machine.ServoMotorStop(0, decTime);
              decTime = MachineSetupData.ServoMotorData[1].DecTime;
              Machine.ServoMotorStop(1, decTime);

              MortorStatusLabel->Caption = "Decrease";
              CameraStatusLabel->Caption = "Standby";
              
              //ShowMessage(THREED_LSF_STUDY_FORM_TEXT_05);
            }
          }
        }
      }
    }
  }
  else
  {
    bSetCaptureCommandSW = false;
    bCaptureSW = false;
    bThreeDLSFStudySW = false;
    bRPMIncreaseWaitingSW = false;
    
    Disk1ExpectedRPM = MachineSetupData.ServoMotorData[0].BaseSpeed * 54.0 / (float)targetSpeed;
    Disk2ExpectedRPM = MachineSetupData.ServoMotorData[1].BaseSpeed * 54.0 / (float)targetSpeed;

    Disk1RealRPM = Machine.GetServoMotorRPM(0);
    Disk2RealRPM = Machine.GetServoMotorRPM(1);

    RefreshDiskSpeed(currentSpeed, Disk1ExpectedRPM, Disk1RealRPM, Disk2ExpectedRPM, Disk2RealRPM);
    RefreshLSFValue(currentSpeed, 0, 0, 0, 0);

    if(Disk1RealRPM < 0.1 && Disk2RealRPM < 0.1)
    {
      timerEnabled = false;
      MotorSpeedControlTimer->Enabled = false;

      RefreshDiskSpeed(0, 0, 0, 0, 0);
      RefreshLSFValue(0, 0, 0, 0, 0);

      SetBlankImage(SD1ThreeDImage, 0);
      SetBlankImage(SD2ThreeDImage, 0);
      SetBlankImage(LSFResultImage, 1);
      SetBlankImage(SD1ResultImage, 1);
      SetBlankImage(SD2ResultImage, 1);
      SetBlankImage(ModelingImage, 1);

      SetResultTable(true);
      globalLSFMaxValue = 0;
      
      SD1ParamsLabel->Caption = "";
      SD2ParamsLabel->Caption = "";

      SetBlankImage(SD1BestCaseThreeDImage, 0);
      SetBlankImage(SD2BestCaseThreeDImage, 0);

      SD1BestParamsLabel->Caption = "";
      SD2BestParamsLabel->Caption = "";

      SD1MatchingRateLabel->Caption = "SD1 Best Matching Rate : NULL";
      SD2MatchingRateLabel->Caption = "SD2 Best Matching Rate : NULL";

      SD1MaxMatchingValue = 0;
      SD2MaxMatchingValue = 0;

      SD1BestMatchingLSF = 0;
      SD2BestMatchingLSF = 0;

      MortorStatusLabel->Caption = "Standby";
      CameraStatusLabel->Caption = "Standby";

      Machine.MachineStop();
      Comm_Request(COMM_HCB, CMD_MACHINE_CAPTURE_STOP);

      Application->ProcessMessages(); 

      AnsiString fileName = ProgramPath.Env + "\\LSFStudyResultTable.lsf";
      WriteDiskLSFTable(fileName);

      SetTableValue(Disk1LSFTableStringGrid, SD1LineScanFrequencyTable, true);
      SetTableValue(Disk2LSFTableStringGrid, SD2LineScanFrequencyTable, true);

      ShowMessage("Complete");

      RefreshComponentStatus(false);
    }
  }

  MotorSpeedControlTimer->Enabled = timerEnabled;
}
//---------------------------------------------------------------------------

void __fastcall TThreeDLineScanFrequencyStudyForm::RunMotor()
{ 
  Comm_Request(COMM_HCB, CMD_MACHINE_CAPTURE_STOP);
  
  int motorIndex;
  int baseSpeed;
  int targetSpeed;
  int accTime;
  int dir;

  // DISK1
  motorIndex = 0;
  baseSpeed = MachineSetupData.ServoMotorData[motorIndex].BaseSpeed;
  targetSpeed = MachineSetupData.ServoMotorData[motorIndex].BaseSpeed * 100 / currentSpeed;
  accTime = MachineSetupData.ServoMotorData[motorIndex].AccTime;
  dir = 0;

  if (MachineSetupData.ServoMotorData[motorIndex].Dir)
  {
    dir = 1;
  }
  else
  {
    dir = 0;
  }
  Machine.ServoMotorRun(motorIndex, baseSpeed, targetSpeed, accTime, dir);

  // DISK2
  motorIndex = 1;
  baseSpeed = MachineSetupData.ServoMotorData[motorIndex].BaseSpeed;
  targetSpeed = MachineSetupData.ServoMotorData[motorIndex].BaseSpeed * 100 / currentSpeed;
  accTime = MachineSetupData.ServoMotorData[motorIndex].AccTime;
  dir = 0;

  if (MachineSetupData.ServoMotorData[motorIndex].Dir)
  {
    dir = 1;
  }
  else
  {
    dir = 0;
  }
  Machine.ServoMotorRun(motorIndex, baseSpeed, targetSpeed, accTime, dir);

  MortorStatusLabel->Caption = "Increase";
  CameraStatusLabel->Caption = "Standby";

  SetBlankImage(SD1BestCaseThreeDImage, 0);
  SetBlankImage(SD2BestCaseThreeDImage, 0);

  SD1BestParamsLabel->Caption = "";
  SD2BestParamsLabel->Caption = "";

  SD1MatchingRateLabel->Caption = "SD1 Best Matching Rate : NULL";
  SD2MatchingRateLabel->Caption = "SD2 Best Matching Rate : NULL";

  SD1MaxMatchingValue = 0;
  SD2MaxMatchingValue = 0;

  SD1BestMatchingLSF = 0;
  SD2BestMatchingLSF = 0;

  CurrentCheckingPosDisplay(Disk1LSFTableStringGrid);
  CurrentCheckingPosDisplay(Disk2LSFTableStringGrid);

  motorSpeedIncreaseTime = Now().Val;
}

//---------------------------------------------------------------------------
void __fastcall TThreeDLineScanFrequencyStudyForm::CloseButtonClick(
      TObject *Sender)
{
  AnsiString fileName = ProgramPath.Env + "\\LSFStudyResultTable.lsf";
  WriteDiskLSFTable(fileName);

  TPCProgramOption tempProgramOption;
  memcpy(&tempProgramOption, &PCProgramOption, sizeof(TPCProgramOption));

  if(SystemParamsCheckBox->Checked)
  {
    tempProgramOption.ApplyLSFTableSW = true;
  }
  else
  {
    tempProgramOption.ApplyLSFTableSW = false;
  }
  
  memcpy(&PCProgramOption, &tempProgramOption, sizeof(TPCProgramOption));
  AnsiString saveFileName = ProgramPath.Env + "\\UserInfor.ini";
	WritePCProgram3DCameraOption(saveFileName, PCProgramOption);
    
  Comm_Request(COMM_HCB, CMD_MACHINE_CAPTURE_STOP);
  this->Close();
}
//---------------------------------------------------------------------------

void __fastcall TThreeDLineScanFrequencyStudyForm::MachineStatusTimerTimer(
      TObject *Sender)
{
  Machine.ReadPortStatus();
}
//---------------------------------------------------------------------------

void __fastcall TThreeDLineScanFrequencyStudyForm::CaptureTimerTimer(
      TObject *Sender)
{
  if(bCaptureSW)
  {
    if(bSetCaptureCommandSW == false)
    {
      MortorStatusLabel->Caption = "Running";
      CameraStatusLabel->Caption = "Send Command";

      if(SD1LSFPosition > SD1MaxLSF && SD2LSFPosition > SD2MaxLSF)
      {
        bCaptureSW = false;
        bThreeDLSFStudySW = true;
        return;
      }

      unsigned int camMask = 0;
      camMask |= (0x0001 << FirstThreeDCameraIndex);
      camMask |= (0x0001 << SecondThreeDCameraIndex);

      float tabletLength = StrToFloat(TabletLengthEdit->Text);
      float tabletHalfLength = StrToFloat(TabletLengthEdit->Text) / 2;

      int targetSpeed = MachineSetupData.ServoMotorData[0].BaseSpeed * 100 / currentSpeed;

      if (SendSPBCaptureSetupForStudy(camMask, tabletHalfLength, NULL, targetSpeed, MachineSetupData.ServoMotorData[SD1_MOTOR_INDEX].BaseSpeed, targetSpeed, MachineSetupData.ServoMotorData[SD2_MOTOR_INDEX].BaseSpeed, 0, 0, 128, SD1LSFPosition, SD2LSFPosition))
      {
        TMachineCaptureSetupData machineCaptureSetupData;

        //memcpy(machineCaptureSetupData.IlluminatorMask, MachineSetupData.MachineCaptureInfo.IlluminatorMask, SYSTEM_MACHINE_CAPTURE_COUNT * sizeof(int));
        memset(machineCaptureSetupData.IlluminatorMask, 0, SYSTEM_MACHINE_CAPTURE_COUNT * sizeof(int));
        machineCaptureSetupData.TabletLength = tabletLength;
	  machineCaptureSetupData.TabletSideThick = ProductData.TabletSideThick*10;
        machineCaptureSetupData.SuctionDiskSpeed = targetSpeed;
        machineCaptureSetupData.StudiedTabletSensorLength = tabletLength / MOTOR_SPEED_TO_LENGTH_CONSTANT_DISC_1 * 20.0 * 1000;
        machineCaptureSetupData.CaptureOption = 1;

        if(Comm_Request(COMM_HCB, CMD_MACHINE_CAPTURE, &machineCaptureSetupData, sizeof(TMachineCaptureSetupData), NULL, 0))
        {
          memset(LSFCheckComplete, 0, SYSTEM_TOTAL_CAMERA_COUNT * sizeof(int));
          bSetCaptureCommandSW = true;

          RefreshLSFValue(currentSpeed, SD1StandardLineScanFrq, SD2StandardLineScanFrq, SD1LSFPosition, SD2LSFPosition);

          int LSFShiftValue = StrToInt(LSFShfitEdit->Text);

          if(StudyResultTestCheckBox->Checked)
          {
            LSFShiftValue = 1;
          }

          SD1LSFPosition += LSFShiftValue;
          SD2LSFPosition += LSFShiftValue;

          captureStartTime = Now().Val;
        }
      }
    }
    else
    {
      CameraStatusLabel->Caption = "Image Upload Waiting";
      
      bool bCaptureExist;

      char cameraIndex;
      unsigned int data[2];
      unsigned int receivedData[2];

      int CaptureWidth = SYSTEM_CAMERA_WIDTH;
      int CaptureHeight = SYSTEM_CAMERA_HEIGHT;

      for(int systemCameraIndex = 0; systemCameraIndex < SYSTEM_TOTAL_CAMERA_COUNT; systemCameraIndex++)
      {
        if((systemCameraIndex == FirstThreeDCameraIndex || systemCameraIndex == SecondThreeDCameraIndex) && LSFCheckComplete[systemCameraIndex] == false)
        {
          int spbIndex = CameraMapInfo[systemCameraIndex].SPBIndex;
          int camIndex = CameraMapInfo[systemCameraIndex].CamIndex;
          data[0] = camIndex;

          try
          {
            if (Comm_IsConnected(COMM_SPB + spbIndex))
            {
              if (!Comm_Request(COMM_SPB + spbIndex, CMD_CAPTURE_EXIST, data, sizeof(unsigned int),
                receivedData, sizeof(unsigned int) * 2))
              {

              }

              if (receivedData[0])
              {
                bCaptureExist = true;
              }
              else
              {
                bCaptureExist = false;
              }

              if (bCaptureExist)
              {
                if (CameraMapInfo[systemCameraIndex].CameraInspectPosition == CAMERA_POSITION_3D)
                {
                  data[1] = SYSTEM_CAMERA_WIDTH_3D * SYSTEM_CAMERA_HEIGHT_3D;
                  if (!Comm_Request(COMM_SPB + spbIndex, CMD_CAPTURE_UPLOAD, data, 2 * sizeof(unsigned int), CaptureData, SYSTEM_CAMERA_WIDTH_3D * SYSTEM_CAMERA_HEIGHT_3D))
                  {

                  }
                }

                TImage *DestImage;
                TImage *BestMatchingImage;
                TLabel *BestMatchingLabel;

                int LSFShiftValue = StrToInt(LSFShfitEdit->Text);

                if(systemCameraIndex == FirstThreeDCameraIndex)
                {
                  DestImage = SD1ThreeDImage;
                  BestMatchingImage = SD1BestCaseThreeDImage;
                  BestMatchingLabel = SD1BestParamsLabel;

                  if (sd1ImageRefresh->Brush->Color == clWhite)
                  {
                    sd1ImageRefresh->Brush->Color = clDkGray;
                  }
                  else
                  {
                    sd1ImageRefresh->Brush->Color = clWhite;
                  }
                }
                else
                {
                  DestImage = SD2ThreeDImage;
                  BestMatchingImage = SD2BestCaseThreeDImage;
                  BestMatchingLabel = SD2BestParamsLabel;

                  if (sd2ImageRefresh->Brush->Color == clWhite)
                  {
                    sd2ImageRefresh->Brush->Color = clDkGray;
                  }
                  else
                  {
                    sd2ImageRefresh->Brush->Color = clWhite;
                  }
                }

                if (CameraMapInfo[systemCameraIndex].CameraInspectPosition == CAMERA_POSITION_3D)
                {
                  Graphics::TBitmap *tempBitmap = new Graphics::TBitmap;
                  tempBitmap->Width = IMAGE_WIDTH_3D;
                  tempBitmap->Height = IMAGE_HEIGHT_3D;
                  tempBitmap->PixelFormat = pf8bit;

                  ImageConversion_3D(tempBitmap, IMAGE_WIDTH_3D, IMAGE_HEIGHT_3D, CaptureData, SYSTEM_CAMERA_WIDTH_3D, SYSTEM_CAMERA_HEIGHT_3D);

                  memset(ThreeDImage, 0, SYSTEM_CAMERA_WIDTH * SYSTEM_CAMERA_HEIGHT);

                  for (int y = 1; y < IMAGE_HEIGHT_3D - 1; y++)
                  {
                    byte *pSrc = (byte *)tempBitmap->ScanLine[y];

                    for (int x = IMAGE_WIDTH_3D * 2 - 1; x >= 0; x--)
                    {
                      ThreeDImage[SYSTEM_CAMERA_WIDTH * y + x] = pSrc[x / 2]; 
                    }
                  }

                  delete tempBitmap;

                  int shapeValue = GetThreeDImageInfo(DestImage);

                  if(systemCameraIndex == FirstThreeDCameraIndex)
                  {
                    if(SD1MaxMatchingValue < shapeValue)
                    {
                      SD1MaxMatchingValue = shapeValue;
                      SD1BestMatchingLSF = SD1LSFPosition - LSFShiftValue;

                      BestMatchingLabel->Caption = "[" + IntToStr(currentSpeed) + "%, LSF : " + IntToStr(SD1LSFPosition - LSFShiftValue) + "]";
                      BestMatchingImage->Picture->Bitmap->Assign(DestImage->Picture->Bitmap);
                      BestMatchingImage->Refresh();

                      SD1MatchingRateLabel->Caption = "SD1 Best Matching Rate : " + IntToStr(SD1MaxMatchingValue) + "%";
                    }

                    SD1ParamsLabel->Caption = "Capture Complete [" + IntToStr(currentSpeed) + "%, LSF : " + IntToStr(SD1LSFPosition - LSFShiftValue) + ", Rate : " + IntToStr(shapeValue) + "%]";
                  }
                  else
                  {
                    if(SD2MaxMatchingValue < shapeValue)
                    {
                      SD2MaxMatchingValue = shapeValue;
                      SD2BestMatchingLSF = SD2LSFPosition - LSFShiftValue;

                      BestMatchingLabel->Caption = "[" + IntToStr(currentSpeed) + "%, LSF : " + IntToStr(SD2LSFPosition - LSFShiftValue) + "]";
                      BestMatchingImage->Picture->Bitmap->Assign(DestImage->Picture->Bitmap);
                      BestMatchingImage->Refresh();

                      SD2MatchingRateLabel->Caption = "SD2 Best Matching Rate : " + IntToStr(SD2MaxMatchingValue) + "%";
                    }

                    SD2ParamsLabel->Caption = "Capture Complete [" + IntToStr(currentSpeed) + "%, LSF : " + IntToStr(SD2LSFPosition - LSFShiftValue) + ", Rate : " + IntToStr(shapeValue) + "%]";
                  }
                }

                LSFCheckComplete[systemCameraIndex] = 1;

                if(LSFCheckComplete[FirstThreeDCameraIndex] && LSFCheckComplete[SecondThreeDCameraIndex])
                {
                  bSetCaptureCommandSW = false;
                  checkTimeForInActive = Now().Val;
                  
                  Comm_Request(COMM_HCB, CMD_MACHINE_CAPTURE_STOP);
                }
              }
            }
          }
          catch (...)
          {
            
          }
        }
      }

      if(!(LSFCheckComplete[FirstThreeDCameraIndex] && LSFCheckComplete[SecondThreeDCameraIndex]))
      {
        double currentCheckTime = Now().Val;

        if((currentCheckTime - captureStartTime) * 24 * 3600 >= 6)
        {
          // TO
          bSetCaptureCommandSW = false;
          checkTimeForInActive = Now().Val;

          int LSFShiftValue = StrToInt(LSFShfitEdit->Text);
          
          if(LSFCheckComplete[FirstThreeDCameraIndex] == false)
          {
            SD1ParamsLabel->Caption = "Timeout [" + IntToStr(currentSpeed) + "%, LSF : " + IntToStr(SD1LSFPosition - LSFShiftValue) + "]";
            SetBlankImage(SD1ThreeDImage, 0);
          }

          if(LSFCheckComplete[SecondThreeDCameraIndex] == false)
          {
            SD2ParamsLabel->Caption = "Timeout [" + IntToStr(currentSpeed) + "%, LSF : " + IntToStr(SD2LSFPosition - LSFShiftValue) + "]";
            SetBlankImage(SD2ThreeDImage, 0);
          }

          Comm_Request(COMM_HCB, CMD_MACHINE_CAPTURE_STOP);
        }
      }
    }
  }
}
//---------------------------------------------------------------------------

void __fastcall TThreeDLineScanFrequencyStudyForm::TabletLengthEditClick(
      TObject *Sender)
{
  TTntEdit *theEdit = (TTntEdit *) Sender;
	KeyboardForm->Text = theEdit->Text;
  if (KeyboardForm->ShowKeypad() == mrOk)
  {
		theEdit->Text = KeyboardForm->Text;
	}
}
//---------------------------------------------------------------------------

bool __fastcall TThreeDLineScanFrequencyStudyForm::SendSPBCaptureSetupForStudy(unsigned int globalCamMask, float tabletHalfLength, int *shutterSpeedList,
	int disk1TargetSpeed, int disk1BaseSpeed, int disk2TargetSpeed, int disk2BaseSpeed, int TestSubsamplingMode, int option, int frameBufferCount, int triggerFreq_SD1, int triggerFreq_SD2)
{
	bool returnValue = true;
	unsigned char camMask[SYSTEM_SPB_MAX_COUNT];
	unsigned short shutterSpeed[SYSTEM_SPB_MAX_COUNT][SYSTEM_SPB_CAMERA_COUNT];
	unsigned short shutterDelay[SYSTEM_SPB_MAX_COUNT][SYSTEM_SPB_CAMERA_COUNT];
	unsigned short triggerFreq[SYSTEM_SPB_MAX_COUNT][SYSTEM_SPB_CAMERA_COUNT];
	unsigned char subsamplingMode[SYSTEM_SPB_MAX_COUNT][SYSTEM_SPB_CAMERA_COUNT];
	TCaptureSetupData captureSetupData;
	captureSetupData.CameraMask = 0;
	captureSetupData.ContainParams = 0;

	for (int spbIndex = 0; spbIndex < MachineSetupData.IPB_BoardCount; spbIndex++)
	{
		camMask[spbIndex] = 0;
	}

	for (int systemCameraIndex = 0; systemCameraIndex < SYSTEM_TOTAL_CAMERA_COUNT; systemCameraIndex++)
	{
		if (globalCamMask & (0x0001 << systemCameraIndex))
		{
			int spbIndex = CameraMapInfo[systemCameraIndex].SPBIndex;
			int camIndex = CameraMapInfo[systemCameraIndex].CamIndex;
			camMask[spbIndex] |= (0x0001 << camIndex);
			if (shutterSpeedList == NULL)
			{
				shutterSpeed[spbIndex][camIndex] = InitialValues.ShutterSpeed[systemCameraIndex];
			}
			else
			{
				shutterSpeed[spbIndex][camIndex] = shutterSpeedList[systemCameraIndex];
			}
		}
	}

	for (int captureIndex = 0; captureIndex < SYSTEM_MACHINE_CAPTURE_COUNT; captureIndex++)
	{
		for (int systemCameraIndex = 0; systemCameraIndex < SYSTEM_TOTAL_CAMERA_COUNT; systemCameraIndex++)
		{
      int spbIndex = CameraMapInfo[systemCameraIndex].SPBIndex;
      int camIndex = CameraMapInfo[systemCameraIndex].CamIndex;
      subsamplingMode[spbIndex][camIndex] = RESOLUTION_NORMAL_QUALITY;

			if (MachineSetupData.MachineCaptureInfo.CamMask[captureIndex] & (0x0001 << systemCameraIndex))
			{
				int spbIndex = CameraMapInfo[systemCameraIndex].SPBIndex;
				int camIndex = CameraMapInfo[systemCameraIndex].CamIndex;

				if (captureIndex < 3)
				{
					if (CameraMapInfo[systemCameraIndex].CameraInspectPosition == CAMERA_POSITION_3D) // 3d camera
					{
						shutterDelay[spbIndex][camIndex] = MachineSetupData.MachineCaptureInfo.IlluminatorDelay[captureIndex]	* disk1TargetSpeed / disk1BaseSpeed;
            triggerFreq[spbIndex][camIndex] = triggerFreq_SD1;
					}
					else
					{
						shutterDelay[spbIndex][camIndex] = MachineSetupData.MachineCaptureInfo.IlluminatorDelay[captureIndex]	* disk1TargetSpeed / disk1BaseSpeed;
						triggerFreq[spbIndex][camIndex] = triggerFreq_SD1;
					}
				}
				else
				{
					if (CameraMapInfo[systemCameraIndex].CameraInspectPosition == CAMERA_POSITION_3D) // 3d camera
					{
						shutterDelay[spbIndex][camIndex] = MachineSetupData.MachineCaptureInfo.IlluminatorDelay[captureIndex]	* disk2TargetSpeed / disk2BaseSpeed;
            triggerFreq[spbIndex][camIndex] = triggerFreq_SD2;
					}
					else
					{
						shutterDelay[spbIndex][camIndex] = MachineSetupData.MachineCaptureInfo.IlluminatorDelay[captureIndex] * disk2TargetSpeed / disk2BaseSpeed;
						triggerFreq[spbIndex][camIndex] = triggerFreq_SD2;
					}
				}
			}
		}
	}

	for (int spbIndex = 0; spbIndex < MachineSetupData.IPB_BoardCount; spbIndex++)
	{
		if (!Comm_IsConnected(COMM_SPB + spbIndex)) continue;
		captureSetupData.CaptureType = CAPTURE_TYPE_FULL_CAPTURE;
		captureSetupData.CaptureMode = CAPTURE_MODE_DEFAULT;
		captureSetupData.FrameBufferCount = frameBufferCount;
		for (int camIndex = 0; camIndex < SYSTEM_SPB_CAMERA_COUNT; camIndex++)
		{
			captureSetupData.ShutterSpeed[camIndex] = shutterSpeed[spbIndex][camIndex];
			captureSetupData.ShutterDelay[camIndex] = shutterDelay[spbIndex][camIndex];
			captureSetupData.TriggerFreq[camIndex] = triggerFreq[spbIndex][camIndex];
			captureSetupData.CameraMask = camMask[spbIndex];
			captureSetupData.SubsamplingMode[camIndex] = subsamplingMode[spbIndex][camIndex];
		}
		captureSetupData.TabletType = 2;
		captureSetupData.SensorLength= 1000;		
		if (!Comm_Request(COMM_SPB + spbIndex, CMD_CAPTURE_SETUP, &captureSetupData, sizeof(TCaptureSetupData),
			NULL, 0))
		{
			returnValue = false;
		}
	}
	return returnValue;
}
//---------------------------------------------------------------------------

void __fastcall TThreeDLineScanFrequencyStudyForm::RefreshLSFValue(int DiskPercentage, int Disk1StdLSF, int Disk2StdLSF, int Disk1LSF, int Disk2LSF)
{
  SD1StdLSFLabel->Caption = IntToStr(Disk1StdLSF);
  SD2StdLSFLabel->Caption = IntToStr(Disk2StdLSF);

  Label16->Caption = "Disk 1(" + IntToStr(DiskPercentage) + "%)";
  Label17->Caption = "Disk 2(" + IntToStr(DiskPercentage) + "%)";

  Disk1SpeedLabel->Caption = "Disk 1(" + IntToStr(DiskPercentage) + "%)";
  Disk2SpeedLabel->Caption = "Disk 2(" + IntToStr(DiskPercentage) + "%)";

  SD1CurrentLSFLabel->Caption = IntToStr(Disk1LSF);
  SD2CurrentLSFLabel->Caption = IntToStr(Disk2LSF);
}
//---------------------------------------------------------------------------

void __fastcall TThreeDLineScanFrequencyStudyForm::SetBlankImage(TImage *dstImage, int option)
{
  Graphics::TBitmap *blankImage = new Graphics::TBitmap;

  if(option == 0)
  {
    blankImage->Width = SYSTEM_CAMERA_WIDTH;
    blankImage->Height = SYSTEM_CAMERA_HEIGHT;
    blankImage->PixelFormat = pf24bit;

    int x, y;
    Byte *ptr;

    for(y = 0; y < SYSTEM_CAMERA_HEIGHT; y++)
    {
      ptr = (Byte*)blankImage->ScanLine[y];
      for(x = 0; x < SYSTEM_CAMERA_WIDTH; x++)
      {
        ptr[3*x + 0] = 0;
        ptr[3*x + 1] = 0;
        ptr[3*x + 2] = 0;
      }
    }
  }
  else
  {
    blankImage->Width = 440;
    blankImage->Height = 530;
    blankImage->PixelFormat = pf24bit;

    int x, y;
    Byte *ptr;

    for(y = 0; y < 530; y++)
    {
      ptr = (Byte*)blankImage->ScanLine[y];
      for(x = 0; x < 440; x++)
      {
        ptr[3*x + 0] = 0;
        ptr[3*x + 1] = 0;
        ptr[3*x + 2] = 0;
      }
    }
  }

  dstImage->Picture->Bitmap->Assign(blankImage);
  dstImage->Refresh();

  delete blankImage;
}
//---------------------------------------------------------------------------
void __fastcall TThreeDLineScanFrequencyStudyForm::RefreshCameraInfo(int cameraIndex)
{
  int Threshold = 0;
  int WriteMask = 0;

   WriteMask |= CAMERA_WRITE_MASK_3D_SH;

  if(CameraMapInfo[cameraIndex].CameraInspectPosition == CAMERA_POSITION_3D)
  {
    Threshold = ProductData.ThresholdFor3D[cameraIndex];
    WriteMask |= CAMERA_WRITE_MASK_TH;
  }

  TROIInfo DefaultROIInfo, ApplyROIInfo;
  AnsiString filePath;
  filePath = ProgramPath.Env + "\\NFACameraInformation.ini";
  DefaultROIInfo = ReadCameraInformation(filePath, cameraIndex);

  // Calibration 화면에서는 Default 값으로 ROI를 설정한다.
  memcpy(&ApplyROIInfo, &DefaultROIInfo, sizeof(TROIInfo));

  WriteMask |= CAMERA_WRITE_MASK_3D_ROI;

  SetCameraInformation(ApplyROIInfo, 10, Threshold, 0, cameraIndex, WriteMask);

  if(CameraMapInfo[cameraIndex].CameraInspectPosition == CAMERA_POSITION_3D)
  {
    Set3DCamera_CaptureParameter(cameraIndex,ThreeDCameraDefaultInformation[cameraIndex].AnalogCameraGain,ThreeDCameraDefaultInformation[cameraIndex].ExposureTime);
  }
}

//---------------------------------------------------------------------------
int __fastcall TThreeDLineScanFrequencyStudyForm::GetThreeDImageInfo(TImage *DestImage)
{
  bool bError = false;
  int x, y;
  int tempAddress;
  int tempAddress2;
  int startX, endX, startY, endY;
  int tabletOutLine[10000 * 2];
  int tabletOutLineCount;

  startX = 20;
  endX = SYSTEM_CAMERA_WIDTH - 20;
  startY = 20;
  endY = SYSTEM_CAMERA_HEIGHT - 20;

  memset(ShapeBinaryImage, 0, SYSTEM_CAMERA_WIDTH * SYSTEM_CAMERA_HEIGHT);
  memset(HalfLabelImage, 0, (SYSTEM_CAMERA_WIDTH / 2) * (SYSTEM_CAMERA_HEIGHT / 2) * sizeof(short));
  memset(smallBinaryImage, 0, (SYSTEM_CAMERA_WIDTH / 2) * (SYSTEM_CAMERA_HEIGHT / 2));

  for(y = startY; y < endY; y++)
  {
    for(x = startX; x < endX; x++)
    {
      tempAddress = SYSTEM_CAMERA_WIDTH * y + x;
      tempAddress2 = (SYSTEM_CAMERA_WIDTH / 2) * (y / 2) + (x / 2);

      if(ThreeDImage[tempAddress] > 10)
      {
        smallBinaryImage[tempAddress2] = 1;
      }
    }
  }

  int maxLabelN = LabellingForTabletImage(HalfLabelImage, smallBinaryImage, startX / 2, endX / 2, startY / 2, endY / 2, SYSTEM_CAMERA_WIDTH / 2, SYSTEM_CAMERA_HEIGHT / 2);

  for(y = startY; y < endY; y++)
  {
    for(x = startX; x < endX; x++)
    {
      tempAddress = SYSTEM_CAMERA_WIDTH * y + x;
      tempAddress2 = (SYSTEM_CAMERA_WIDTH / 2) * (y / 2) + (x / 2);

      if(HalfLabelImage[tempAddress2] == maxLabelN)
      {
        ShapeBinaryImage[tempAddress] = 1;
      }
    }
  }

  int minX, maxX;
  for (y = startY; y < endY; y++)
  {
    minX = SYSTEM_CAMERA_WIDTH;
    maxX = 0;

    for (x = startX; x < endX; x++)
    {
      tempAddress = y * SYSTEM_CAMERA_WIDTH + x;

      if(ShapeBinaryImage[tempAddress])
      {
        if(minX > x) minX = x;
        if(maxX < x) maxX = x;
      }
    }

    for (x = minX; x <= maxX; x++)
    {
      tempAddress = y * SYSTEM_CAMERA_WIDTH + x;
      ShapeBinaryImage[tempAddress] = 1;
    }
  }

  int tabletCenterX, tabletCenterY, tempCount;
  int accHeight;
  int avgHeight;

  accHeight = 0;
  tabletCenterX = tabletCenterY = tempCount = 0;
  for(y = startY; y < endY; y++)
  {
    for(x = startX; x < endX; x++)
    {
      tempAddress = y * SYSTEM_CAMERA_WIDTH + x;
      if(ShapeBinaryImage[tempAddress])
      {
        tabletCenterX += x;
        tabletCenterY += y;
        tempCount++;

        accHeight += ThreeDImage[tempAddress];
      }
    }
  }

  if(tempCount)
  {
    tabletCenterX /= tempCount;
    tabletCenterY /= tempCount;
    avgHeight = accHeight / tempCount;
  }
  else
  {
    bError = true;
  }

  int matchingRate = 0;

  if(!bError)
  {
    int tabletOutLine[10000 * 2];
    tabletOutLineCount = 0;

    for(y = startY; y < endY; y++)
    {
      for(x = startX; x < endX; x++)
      {
        tempAddress = y * SYSTEM_CAMERA_WIDTH + x;
        if(ShapeBinaryImage[tempAddress])
        {
          if(ShapeBinaryImage[tempAddress - 2] == 0 ||
              ShapeBinaryImage[tempAddress + 2] == 0 ||
              ShapeBinaryImage[tempAddress - 2 * SYSTEM_CAMERA_WIDTH] == 0 ||
              ShapeBinaryImage[tempAddress + 2 * SYSTEM_CAMERA_WIDTH] == 0)
          {
            if(tabletOutLineCount < 10000)
            {
              tabletOutLine[tabletOutLineCount * 2 + 0] = x;
              tabletOutLine[tabletOutLineCount * 2 + 1] = y;
              tabletOutLineCount++;
            }
          }
        }
      }
    }

    DestImage->Picture->Bitmap->Width = IMAGE_WIDTH_3D * 2;
    DestImage->Picture->Bitmap->Height = IMAGE_HEIGHT_3D;
    DestImage->Picture->Bitmap->PixelFormat = pf24bit;

    Byte *ptr;

    for(y = 0; y < SYSTEM_CAMERA_HEIGHT; y++)
    {
      ptr = (Byte*)DestImage->Picture->Bitmap->ScanLine[y];
      for(x = 0; x < SYSTEM_CAMERA_WIDTH; x++)
      {
        tempAddress = y * SYSTEM_CAMERA_WIDTH + x;

        ptr[3 * x + 0] = ThreeDImage[tempAddress];
        ptr[3 * x + 1] = ThreeDImage[tempAddress];
        ptr[3 * x + 2] = ThreeDImage[tempAddress];

        if(ShapeBinaryImage[tempAddress])
        {
          ptr[3 * x + 0] = min(255, ThreeDImage[tempAddress] * 1.5);
          ptr[3 * x + 1] = min(255, ThreeDImage[tempAddress] * 1.5);
          ptr[3 * x + 2] = min(255, ThreeDImage[tempAddress] * 1.5);
        }
      }
    }

    for(int m = 0; m < tabletOutLineCount; m++)
    {
      x = tabletOutLine[m * 2 + 0];
      y = tabletOutLine[m * 2 + 1];

      ptr = (Byte*)DestImage->Picture->Bitmap->ScanLine[y];

      ptr[3 * x + 0] = 0;
      ptr[3 * x + 1] = 0;
      ptr[3 * x + 2] = 255;
    }

    for(int tempY = tabletCenterY - 1; tempY <= tabletCenterY + 1; tempY++)
    {
      for(int tempX = tabletCenterX - 1; tempX <= tabletCenterX + 1; tempX++)
      {
        ptr = (Byte*)DestImage->Picture->Bitmap->ScanLine[tempY];

        ptr[3 * tempX + 0] = 0;
        ptr[3 * tempX + 1] = 0;
        ptr[3 * tempX + 2] = 255;
      }
    }

    DestImage->Refresh();

    int accLength, length;
    accLength = tempCount = 0;

    int lengthTable[10000];
    memset(lengthTable, 0, sizeof(int) * 10000);

    for(int m = 0; m < tabletOutLineCount; m++)
    {
      x = tabletOutLine[m * 2 + 0];
      y = tabletOutLine[m * 2 + 1];

      length = (x - tabletCenterX) * (x - tabletCenterX) + (y - tabletCenterY) * (y - tabletCenterY);
      lengthTable[m] = length;

      accLength += length;
      tempCount++;      
    }

    int avgLength = 0;
    if(tempCount)
    {
      avgLength = accLength / tempCount;
    }

    int resultSum = 0;
    for(int m = 0; m < tabletOutLineCount; m++)
    {
      int errorRate = min(1000, abs(1000 - lengthTable[m] * 1000 / avgLength));
      resultSum += (1000 - errorRate);
    }

    matchingRate = resultSum / tabletOutLineCount; 

    return matchingRate;
  }
  else
  {
    SetBlankImage(DestImage, 0);
    
    return matchingRate;
  }
}

//---------------------------------------------------------------------------
int __fastcall TThreeDLineScanFrequencyStudyForm::LabellingForTabletImage(short *label_Image, unsigned char *smallSizeBinaryImage, int startX, int endX, int startY, int endY, int imageWidth, int imageHeight)
{
	int i, x, y;
	int temp_address;
	int currentLabel;
	int new_count;
	int newLval;
	int temp;
	int tempAddress;
	int maxLabelN;
	int maxLabelCount;
	int breakSW;

  short CollisionArray[MAX_LABEL_COUNT];
  unsigned short Pixel_N_Of_Label[MAX_LABEL_COUNT];
  short StartX_Label[MAX_LABEL_COUNT];
  short EndX_Label[MAX_LABEL_COUNT];
  short StartY_Label[MAX_LABEL_COUNT];
  short EndY_Label[MAX_LABEL_COUNT];

	currentLabel = 0;

	for (i = 0; i < MAX_LABEL_COUNT; i++)
	{
		CollisionArray[i] = i;
		Pixel_N_Of_Label[i] = 0;
		StartX_Label[i] = 640;
		EndX_Label[i] = 0;
		StartY_Label[i] = 640;
		EndY_Label[i] = 0;
	}

	breakSW = 0;
	for (y = startY; y < endY; y++)
	{
		for (x = startX; x < endX; x++)
		{
			temp_address = imageWidth * (y)+x;
			if (*(smallSizeBinaryImage + temp_address))
			{
				if (*(label_Image + temp_address - imageWidth) == 0)     // up is background
				{
					if (*(label_Image + temp_address - 1) == 0)       // left is also background
					{
						currentLabel++;
						*(label_Image + temp_address) = currentLabel; // first label number is '1'
					}
					else                        // left is not background
					{
						*(label_Image + temp_address) = *(label_Image + temp_address - 1);
					}
				}
				else                // up is not background
				{
					if (*(label_Image + temp_address - 1) == 0)       // left is background
					{
						*(label_Image + temp_address) = *(label_Image + temp_address - imageWidth);
					}
					else                        // left is not background
					{
						if (*(label_Image + temp_address - imageWidth) == *(label_Image + temp_address - 1))
						{
							*(label_Image + temp_address) = *(label_Image + temp_address - imageWidth);
						}
						else        // collision detected
						{           // Left label이 Up label보다 항상 크다
							CollisionArray[max(*(label_Image + temp_address - 1), *(label_Image + temp_address - imageWidth))] =
								min(*(label_Image + temp_address - 1), *(label_Image + temp_address - imageWidth));
							*(label_Image + temp_address) = CollisionArray[*(label_Image + temp_address - imageWidth)];
						}
					}
				}
				if (currentLabel >= MAX_LABEL_COUNT - 1)
				{
					breakSW = 1;
					break;
				}
			}
		}
		if (breakSW == 1)
			break;
	}

	newLval = 1;
	for (i = 1; i <= currentLabel; i++)
	{
		if (CollisionArray[CollisionArray[i]] > newLval)
		{
			newLval++;
			CollisionArray[i] = newLval;
		}
		else
		{
			CollisionArray[i] = CollisionArray[CollisionArray[i]];
		}
	}

	if (newLval > 500)  newLval = 499;
	new_count = newLval;

	for (y = startY; y < endY; y++)
	{
		for (x = startX; x < endX; x++)
		{
			tempAddress = imageWidth * (y)+x;
			if (*(smallSizeBinaryImage + tempAddress))
			{
				temp = *(label_Image + tempAddress);
				temp = CollisionArray[temp];
				*(label_Image + tempAddress) = temp;
				Pixel_N_Of_Label[temp]++;
				if (StartX_Label[temp] > x)  StartX_Label[temp] = x;
				if (StartY_Label[temp] > y)  StartY_Label[temp] = y;
				if (EndX_Label[temp] < x)  EndX_Label[temp] = x;
				if (EndY_Label[temp] < y)  EndY_Label[temp] = y;
			}
		}
	}

	maxLabelN = 0;
	maxLabelCount = 0;
	for (i = 1; i <= new_count; i++)
	{
		if (Pixel_N_Of_Label[i] > maxLabelCount)
		{
			maxLabelN = i;
			maxLabelCount = Pixel_N_Of_Label[i];
		}
	}
  
	return(maxLabelN);
}
//---------------------------------------------------------------------------


void __fastcall TThreeDLineScanFrequencyStudyForm::LSFRangeEditClick(
      TObject *Sender)
{
  TTntEdit *theEdit = (TTntEdit *) Sender;
	KeyboardForm->Text = theEdit->Text;
  if (KeyboardForm->ShowKeypad() == mrOk)
  {
		theEdit->Text = KeyboardForm->Text;
	}  
}
//---------------------------------------------------------------------------

void __fastcall TThreeDLineScanFrequencyStudyForm::LSFShfitEditClick(
      TObject *Sender)
{
  TTntEdit *theEdit = (TTntEdit *) Sender;
	KeyboardForm->Text = theEdit->Text;
  if (KeyboardForm->ShowKeypad() == mrOk)
  {
		theEdit->Text = KeyboardForm->Text;

    int shiftValue = StrToInt(theEdit->Text);

    if(shiftValue < 1)
    {
      shiftValue = 1;

      theEdit->Text = IntToStr(shiftValue);
    }
	}
}
//---------------------------------------------------------------------------

void __fastcall TThreeDLineScanFrequencyStudyForm::LSFRangeEditKeyPress(
      TObject *Sender, char &Key)
{
  Key = NULL;  
}
//---------------------------------------------------------------------------

bool __fastcall TThreeDLineScanFrequencyStudyForm::ReadDiskLSFTable(AnsiString fileName)
{
  if(!FileExists(fileName))
  {
    return false;
  }

  TFileStream *fileStream = new TFileStream(fileName, fmOpenRead);
  if (fileStream)
  {
    fileStream->Read(SD1LineScanFrequencyTable, sizeof(int) * MAX_MOTOR_SPEED);
    fileStream->Read(SD2LineScanFrequencyTable, sizeof(int) * MAX_MOTOR_SPEED);
    delete fileStream;
  }
  else
  {
    return false;
  }

	return true;
}

//---------------------------------------------------------------------------

bool __fastcall TThreeDLineScanFrequencyStudyForm::WriteDiskLSFTable(AnsiString fileName)
{
  TFileStream *fileStream = new TFileStream(fileName, fmCreate);
	if (fileStream)
	{
    fileStream->Write(SD1LineScanFrequencyTable, sizeof(int) * MAX_MOTOR_SPEED);
    fileStream->Write(SD2LineScanFrequencyTable, sizeof(int) * MAX_MOTOR_SPEED);

		delete fileStream;
	}
  else
  {
    return false;
  }
	
	return true;
}

//---------------------------------------------------------------------------
void __fastcall TThreeDLineScanFrequencyStudyForm::MakeDefaultTable(TStringGrid *tempGrid)
{
  int hSpace;
  int wSpace;

  wSpace = tempGrid->Width - tempGrid->ColWidths[0] - 25;

  for(int m = 1; m <= 10; m++)
  {
    tempGrid->ColWidths[m] = wSpace / 10;
  }

  hSpace = tempGrid->Height - tempGrid->RowHeights[0] - 25;

  for(int m = 1; m <= 20; m++)
  {
    tempGrid->RowHeights[m] = hSpace / 20;
  }

  for(int m = 1; m <= 10; m++)
  {
    tempGrid->Cells[m][0] = IntToStr(m) + "%";
  }

  for(int m = 1; m <= 20; m++)
  {
    tempGrid->Cells[0][m] = "+ " + IntToStr((m - 1) * 10) + "%";
  }

  for(int m = 1; m <= 20; m++)
  {
    for(int n = 1; n <= 10; n++)
    {
      tempGrid->Cells[n][m] = "N/A";
    }
  }
}

//---------------------------------------------------------------------------
void __fastcall TThreeDLineScanFrequencyStudyForm::SetTableValue(TStringGrid *tempGrid, int *value, bool All)
{
  int column;
  int row;
  int SpeedPercentage;

  if(All)
  {
    for(int m = 1; m < MAX_MOTOR_SPEED; m++)
    {
      SpeedPercentage = MachineSetupData.ServoMotorData[0].BaseSpeed * 100 / m;

      if(SpeedPercentage >= 1 && SpeedPercentage <= 200)
      {
        column = SpeedPercentage % 10 + (SpeedPercentage % 10 == 0) * 10;
        row = (SpeedPercentage - 1) / 10 + 1;

        if(value[m])
        {
          tempGrid->Cells[column][row] = IntToStr(value[m]);
        }
        else
        {
          tempGrid->Cells[column][row] = "N/A";
        }
      }
    }
  }
  else
  {
    SpeedPercentage = currentSpeed;
    int m = MachineSetupData.ServoMotorData[0].BaseSpeed * 100 / SpeedPercentage;

    if(SpeedPercentage >= 1 && SpeedPercentage <= 200)
    {
      column = SpeedPercentage % 10 + (SpeedPercentage % 10 == 0) * 10;
      row = (SpeedPercentage - 1) / 10 + 1;

      if(value[m])
      {
        tempGrid->Cells[column][row] = IntToStr(value[m]);
      }
      else
      {
        tempGrid->Cells[column][row] = "N/A";
      }
    }
  }
}
//---------------------------------------------------------------------------
void __fastcall TThreeDLineScanFrequencyStudyForm::Button1Click(
      TObject *Sender)
{
  memset(ThreeDImage, 0, SYSTEM_CAMERA_WIDTH * SYSTEM_CAMERA_HEIGHT);
  
  for (int y = 1; y < IMAGE_HEIGHT_3D - 1; y++)
  {
    byte *pSrc = (byte *)Image1->Picture->Bitmap->ScanLine[y];

    for (int x = IMAGE_WIDTH_3D * 2 - 1; x >= 0; x--)
    {
      ThreeDImage[SYSTEM_CAMERA_WIDTH * y + x] = pSrc[3*x + 1];
    }
  }

  GetThreeDImageInfo(SD1ThreeDImage);
}
//---------------------------------------------------------------------------

void __fastcall TThreeDLineScanFrequencyStudyForm::CurrentCheckingPosDisplay(TStringGrid *tempGrid)
{
  int column;
  int row;
  int SpeedPercentage;

  SpeedPercentage = currentSpeed;
  int m = MachineSetupData.ServoMotorData[0].BaseSpeed * 100 / SpeedPercentage;

  if(SpeedPercentage >= 1 && SpeedPercentage <= 200)
  {
    column = SpeedPercentage % 10 + (SpeedPercentage % 10 == 0) * 10;
    row = (SpeedPercentage - 1) / 10 + 1;

    tempGrid->Cells[column][row] = "Checking...";
  }
}
//---------------------------------------------------------------------------


void __fastcall TThreeDLineScanFrequencyStudyForm::LoadResultButtonClick(
      TObject *Sender)
{
  globalLSFMaxValue = 0;

  for(int m = 0; m < MAX_MOTOR_SPEED; m++)
  {
    if(globalLSFMaxValue < SD1LineScanFrequencyTable[m])
    {
      globalLSFMaxValue = SD1LineScanFrequencyTable[m];
    }

    if(globalLSFMaxValue < SD2LineScanFrequencyTable[m])
    {
      globalLSFMaxValue = SD2LineScanFrequencyTable[m];
    }
  }

  if(globalLSFMaxValue == 0)
  {
    ShowMessage("Invalid Value");
    return;
  }

  SetBlankImage(SD1ResultImage, 1);
  SetBlankImage(SD2ResultImage, 1);

  int ImageWidth = 440;
  int ImageHeight = 490;
  int targetSpeed, targetSpeed2;

  int x, y;
  int Xspace = 20;
  int Yspace = 20;
  int nextX, nextY;

  Byte *ptr;

  for(int m = 1; m < MAX_MOTOR_SPEED; m++)
  {
    targetSpeed = MachineSetupData.ServoMotorData[0].BaseSpeed * 100 / m;

    if(SD1LineScanFrequencyTable[m])
    {
      x = Xspace + targetSpeed * 2;
      y = Yspace + SD1LineScanFrequencyTable[m] * ImageHeight / globalLSFMaxValue;

      int nextSearchSW = 0;
      for(int n = m + 1; n < MAX_MOTOR_SPEED; n++)
      {
        if(SD1LineScanFrequencyTable[n])
        {
          targetSpeed2 = MachineSetupData.ServoMotorData[0].BaseSpeed * 100 / n;

          nextX = Xspace + targetSpeed2 * 2;
          nextY = Yspace + SD1LineScanFrequencyTable[n] * ImageHeight / globalLSFMaxValue;

          nextSearchSW = 1;
          break;
        }
      }

      if(nextSearchSW == 0)
      {
        nextX = nextY = Xspace;
      }

      if(x > 2 && x < 440 - 2 && y > 2 && y < 530 - 2 &&
          nextX > 2 && nextX < 440 - 2 && nextY > 2 && nextY < 530 - 2)
      {
        SD1ResultImage->Canvas->Pen->Width = 2;
        SD1ResultImage->Canvas->Pen->Color = clRed;
        if(nextSearchSW)
        {
          SD1ResultImage->Canvas->MoveTo(x, y);
          SD1ResultImage->Canvas->LineTo(nextX, nextY);
        }
        else
        {
          SD1ResultImage->Canvas->MoveTo(x, y);
          SD1ResultImage->Canvas->LineTo(x, y + 1);
        }
      }
    }

    if(SD2LineScanFrequencyTable[m])
    {
      x = Xspace + targetSpeed * 2;
      y = Yspace + SD2LineScanFrequencyTable[m] * ImageHeight / globalLSFMaxValue;

      int nextSearchSW = 0;
      for(int n = m + 1; n < MAX_MOTOR_SPEED; n++)
      {
        if(SD2LineScanFrequencyTable[n])
        {
          targetSpeed2 = MachineSetupData.ServoMotorData[0].BaseSpeed * 100 / n;

          nextX = Xspace + targetSpeed2 * 2;
          nextY = Yspace + SD2LineScanFrequencyTable[n] * ImageHeight / globalLSFMaxValue;

          nextSearchSW = 1;
          break;
        }
      }

      if(nextSearchSW == 0)
      {
        nextX = nextY = Xspace;
      }

      if(x > 2 && x < 440 - 2 && y > 2 && y < 530 - 2 &&
          nextX > 2 && nextX < 440 - 2 && nextY > 2 && nextY < 530 - 2)
      {
        SD2ResultImage->Canvas->Pen->Width = 2;
        SD2ResultImage->Canvas->Pen->Color = clBlue;
        if(nextSearchSW)
        {
          SD2ResultImage->Canvas->MoveTo(x, y);
          SD2ResultImage->Canvas->LineTo(nextX, nextY);
        }
        else
        {
          SD2ResultImage->Canvas->MoveTo(x, y);
          SD2ResultImage->Canvas->LineTo(x, y);
        }
      }
    }
  }

  SD1ResultImage->Refresh();
  SD2ResultImage->Refresh();

  SetBlankImage(ModelingImage, 1);

  LSFModelingFunc();

  for(int m = 1; m < MAX_MOTOR_SPEED; m++)
  {
    targetSpeed = MachineSetupData.ServoMotorData[0].BaseSpeed * 100 / m;

    if(LSFModelingTable[m])
    {
      x = Xspace + targetSpeed * 2;
      y = Yspace + LSFModelingTable[m] * ImageHeight / globalLSFMaxValue;

      int nextSearchSW = 0;
      for(int n = m + 1; n < MAX_MOTOR_SPEED; n++)
      {
        if(LSFModelingTable[n])
        {
          targetSpeed2 = MachineSetupData.ServoMotorData[0].BaseSpeed * 100 / n;

          nextX = Xspace + targetSpeed2 * 2;
          nextY = Yspace + LSFModelingTable[n] * ImageHeight / globalLSFMaxValue;

          nextSearchSW = 1;
          break;
        }
      }

      if(nextSearchSW == 0)
      {
        nextX = nextY = Xspace;
      }

      if(x > 2 && x < 440 - 2 && y > 2 && y < 530 - 2 &&
          nextX > 2 && nextX < 440 - 2 && nextY > 2 && nextY < 530 - 2)
      {
        ModelingImage->Canvas->Pen->Width = 2;
        ModelingImage->Canvas->Pen->Color = clGreen;
        if(nextSearchSW)
        {
          ModelingImage->Canvas->MoveTo(x, y);
          ModelingImage->Canvas->LineTo(nextX, nextY);
        }
        else
        {
          ModelingImage->Canvas->MoveTo(x, y);
          ModelingImage->Canvas->LineTo(x, y);
        }
      }
    }
  }
  ModelingImage->Refresh();

  ViewStudyResult(false, NULL, NULL);
  SetResultTable(false);

  ApplyLSFModelBtn->Enabled = true;
}
//---------------------------------------------------------------------------


void __fastcall TThreeDLineScanFrequencyStudyForm::LSFModelingFunc()
{
  memset(LSFModelingTable, 0, sizeof(int) * MAX_MOTOR_SPEED);

  long long srcLSFTable[MAX_MOTOR_SPEED_PERCENTAGE];
  memset(srcLSFTable, 0, sizeof(long long) * MAX_MOTOR_SPEED_PERCENTAGE);

  int sumLSFValue, tempCount;
  int startSpeed;
  int endSpeed;

  startSpeed = 0xFFFF;
  endSpeed = 0;

  for(int m = 0; m < MAX_MOTOR_SPEED; m++)
  {
    sumLSFValue = tempCount = 0;

    if(SD1LineScanFrequencyTable[m])
    {
      sumLSFValue += SD1LineScanFrequencyTable[m];
      tempCount++;
    }

    if(SD2LineScanFrequencyTable[m])
    {
      sumLSFValue += SD2LineScanFrequencyTable[m];
      tempCount++;
    }

    if(tempCount)
    {
      int targetSpeed = MachineSetupData.ServoMotorData[0].BaseSpeed * 100 / m;
      
      srcLSFTable[targetSpeed] = sumLSFValue / tempCount;

      if(startSpeed > targetSpeed)
      {
        startSpeed = targetSpeed;
      }

      if(endSpeed < targetSpeed)
      {
        endSpeed = targetSpeed;
      }
    }
  }

  long long DiskSpeedMatr[MAX_MOTOR_SPEED_PERCENTAGE][3];
  long long DiskSpeedPositionTransMat[3][MAX_MOTOR_SPEED_PERCENTAGE];
  long long LSFMat[MAX_MOTOR_SPEED_PERCENTAGE];
  long long squareMultipleMat[3][3];
  long long inverseMat[3][3];
  long long resultMultipleMat[3][MAX_MOTOR_SPEED_PERCENTAGE];
  long long detA;
  int Value;
  const int MAX_MODEL_COUNT = 100;

  int ModelDiff = abs(endSpeed - startSpeed);
  int startModel, endModel;

  int OutofModelCount = 0;
  if(ModelDiff > MAX_MODEL_COUNT)
  {
    OutofModelCount = ModelDiff - MAX_MODEL_COUNT;
  }

  memset(modelingCount, 0, sizeof(int) * MAX_MOTOR_SPEED);
  
  for(int tagetModelGrp = 0; tagetModelGrp <= OutofModelCount; tagetModelGrp++)
  {
    memset(DiskSpeedMatr, 0, sizeof(long long) * MAX_MOTOR_SPEED_PERCENTAGE * 3);
    memset(DiskSpeedPositionTransMat, 0, sizeof(long long) * MAX_MOTOR_SPEED_PERCENTAGE * 3);
    memset(LSFMat, 0, sizeof(long long) * MAX_MOTOR_SPEED_PERCENTAGE);
    memset(squareMultipleMat, 0, sizeof(long long) * 3 * 3);
    memset(inverseMat, 0, sizeof(long long) * 3 * 3);
    memset(resultMultipleMat, 0, sizeof(long long) * 3 * MAX_MOTOR_SPEED_PERCENTAGE);

    startModel = startSpeed + tagetModelGrp;
    endModel = startModel + MAX_MODEL_COUNT;

    if(endModel > endSpeed)
    {
      endModel = endSpeed;
    } 

    for(int m = startModel; m <= endModel; m++)
    {
      Value = srcLSFTable[m];

      if(Value)
      {
        DiskSpeedMatr[m][0] = m * m;
        DiskSpeedMatr[m][1] = m;
        DiskSpeedMatr[m][2] = 1;

        DiskSpeedPositionTransMat[0][m] = m * m;
        DiskSpeedPositionTransMat[1][m] = m;
        DiskSpeedPositionTransMat[2][m] = 1;

        LSFMat[m] = Value;
      }
    }

    for(int m = startModel; m <= endModel; m++)
    {
      squareMultipleMat[0][0] += DiskSpeedPositionTransMat[0][m] * DiskSpeedMatr[m][0];
      squareMultipleMat[0][1] += DiskSpeedPositionTransMat[0][m] * DiskSpeedMatr[m][1];
      squareMultipleMat[0][2] += DiskSpeedPositionTransMat[0][m] * DiskSpeedMatr[m][2];

      squareMultipleMat[1][0] += DiskSpeedPositionTransMat[1][m] * DiskSpeedMatr[m][0];
      squareMultipleMat[1][1] += DiskSpeedPositionTransMat[1][m] * DiskSpeedMatr[m][1];
      squareMultipleMat[1][2] += DiskSpeedPositionTransMat[1][m] * DiskSpeedMatr[m][2];

      squareMultipleMat[2][0] += DiskSpeedPositionTransMat[2][m] * DiskSpeedMatr[m][0];
      squareMultipleMat[2][1] += DiskSpeedPositionTransMat[2][m] * DiskSpeedMatr[m][1];
      squareMultipleMat[2][2] += DiskSpeedPositionTransMat[2][m] * DiskSpeedMatr[m][2];
    }
  
    detA =   squareMultipleMat[0][0] * (squareMultipleMat[1][1] * squareMultipleMat[2][2] - squareMultipleMat[1][2] * squareMultipleMat[2][1])
           - squareMultipleMat[0][1] * (squareMultipleMat[1][0] * squareMultipleMat[2][2] - squareMultipleMat[1][2] * squareMultipleMat[2][0])
           + squareMultipleMat[0][2] * (squareMultipleMat[1][0] * squareMultipleMat[2][1] - squareMultipleMat[1][1] * squareMultipleMat[2][0]);

    if(detA == 0)
    {
      return;
    }

    inverseMat[0][0] = (squareMultipleMat[1][1] * squareMultipleMat[2][2] - squareMultipleMat[2][1] * squareMultipleMat[1][2]);
    inverseMat[0][1] = (squareMultipleMat[0][2] * squareMultipleMat[2][1] - squareMultipleMat[0][1] * squareMultipleMat[2][2]);
    inverseMat[0][2] = (squareMultipleMat[0][1] * squareMultipleMat[1][2] - squareMultipleMat[0][2] * squareMultipleMat[1][1]);

    inverseMat[1][0] = (squareMultipleMat[1][2] * squareMultipleMat[2][0] - squareMultipleMat[1][0] * squareMultipleMat[2][2]);
    inverseMat[1][1] = (squareMultipleMat[0][0] * squareMultipleMat[2][2] - squareMultipleMat[0][2] * squareMultipleMat[2][0]);
    inverseMat[1][2] = (squareMultipleMat[1][0] * squareMultipleMat[0][2] - squareMultipleMat[0][0] * squareMultipleMat[1][2]);

    inverseMat[2][0] = (squareMultipleMat[1][0] * squareMultipleMat[2][1] - squareMultipleMat[2][0] * squareMultipleMat[1][1]);
    inverseMat[2][1] = (squareMultipleMat[2][0] * squareMultipleMat[0][1] - squareMultipleMat[0][0] * squareMultipleMat[2][1]);
    inverseMat[2][2] = (squareMultipleMat[0][0] * squareMultipleMat[1][1] - squareMultipleMat[1][0] * squareMultipleMat[0][1]);

    for(int m = startModel; m <= endModel; m++)
    {
      resultMultipleMat[0][m] += (inverseMat[0][0] * DiskSpeedPositionTransMat[0][m] + inverseMat[0][1] * DiskSpeedPositionTransMat[1][m] + inverseMat[0][2] * DiskSpeedPositionTransMat[2][m]);
      resultMultipleMat[1][m] += (inverseMat[1][0] * DiskSpeedPositionTransMat[0][m] + inverseMat[1][1] * DiskSpeedPositionTransMat[1][m] + inverseMat[1][2] * DiskSpeedPositionTransMat[2][m]);
      resultMultipleMat[2][m] += (inverseMat[2][0] * DiskSpeedPositionTransMat[0][m] + inverseMat[2][1] * DiskSpeedPositionTransMat[1][m] + inverseMat[2][2] * DiskSpeedPositionTransMat[2][m]);
    }

    double modelingConstA, modelingConstB, modelingConstC;
    modelingConstA = modelingConstB = modelingConstC = 0;

    for(int m = startSpeed; m <= endSpeed; m++)
    {
      modelingConstA += resultMultipleMat[0][m] * LSFMat[m];
      modelingConstB += resultMultipleMat[1][m] * LSFMat[m];
      modelingConstC += resultMultipleMat[2][m] * LSFMat[m];
    }
  
    modelingConstA /= (float)detA;
    modelingConstB /= (float)detA;
    modelingConstC /= (float)detA;

    double nLSFValue;
    for(int m = startModel; m <= endModel; m++)
    {
      Value = srcLSFTable[m];
      nLSFValue = modelingConstA * m * m + modelingConstB * m + modelingConstC;

      if(m)
      {
        int targetSpeed = MachineSetupData.ServoMotorData[0].BaseSpeed * 100 / m;

        if(nLSFValue > 0)
        {
          LSFModelingTable[targetSpeed] += (int) nLSFValue;
          modelingCount[targetSpeed]++;
        }
        else
        {
          LSFModelingTable[targetSpeed] = 0;
        }
      }
    }
  }

  for(int m = 0; m < MAX_MOTOR_SPEED; m++)
  {
    if(modelingCount[m])
    {
      LSFModelingTable[m] /= modelingCount[m];
    }
  }
}

//---------------------------------------------------------------------------
void __fastcall TThreeDLineScanFrequencyStudyForm::ViewStudyResult(bool bNavigatorSW, int nX, int nY)
{
  SetBlankImage(LSFResultImage, 1);

  Graphics::TBitmap *graphImage = new Graphics::TBitmap;

  graphImage->Width = 440;
  graphImage->Height = 530;
  graphImage->PixelFormat = pf24bit;

  int x, y;
  Byte *ptr;
  Byte *src1;
  Byte *src2;
  Byte *src3;

  int value;
  for(y = 0; y < 530; y++)
  {
    ptr = (Byte*)graphImage->ScanLine[y];

    src1 = (Byte*)SD1ResultImage->Picture->Bitmap->ScanLine[y];
    src2 = (Byte*)SD2ResultImage->Picture->Bitmap->ScanLine[y];
    src3 = (Byte*)ModelingImage->Picture->Bitmap->ScanLine[y];
    
    for(x = 0; x < 440; x++)
    {
      ptr[3*x + 0] = 0;
      ptr[3*x + 1] = 0;
      ptr[3*x + 2] = 0;

      if(CheckBox1->Checked)
      {
        value = max(src1[3*x + 0], max(src1[3*x + 1], src1[3*x + 2]));

        if(value)
        {
          ptr[3*x + 0] = 0;
          ptr[3*x + 1] = 0;
          ptr[3*x + 2] = 255;
        }
      }

      if(CheckBox2->Checked)
      {
        value = max(src2[3*x + 0], max(src2[3*x + 1], src2[3*x + 2]));

        if(value)
        {
          ptr[3*x + 0] = 255;
          ptr[3*x + 1] = 0;
          ptr[3*x + 2] = 0;
        }
      }

      if(CheckBox3->Checked)
      {
        value = max(src3[3*x + 0], max(src3[3*x + 1], src3[3*x + 2]));

        if(value)
        {
          ptr[3*x + 0] = 0;
          ptr[3*x + 1] = 255;
          ptr[3*x + 2] = 0;
        }
      }
    }
  }

  LSFResultImage->Picture->Bitmap->Assign(graphImage);

  if(bNavigatorSW)
  {
    LSFResultImage->Canvas->Pen->Width = 1;
    LSFResultImage->Canvas->Pen->Color = clYellow;

    LSFResultImage->Canvas->MoveTo(nX, 0);
    LSFResultImage->Canvas->LineTo(nX, 530);

    LSFResultImage->Canvas->MoveTo(0, nY);
    LSFResultImage->Canvas->LineTo(440, nY);
  }

  LSFResultImage->Refresh();

  delete graphImage;
}
//---------------------------------------------------------------------------
void __fastcall TThreeDLineScanFrequencyStudyForm::CheckBox1Click(
      TObject *Sender)
{
  ViewStudyResult(false, NULL, NULL);
}
//---------------------------------------------------------------------------

void __fastcall TThreeDLineScanFrequencyStudyForm::SetResultTable(bool Default)
{  
  ResultStringGrid->RowCount = 2;

  for(int m = 0; m < ResultStringGrid->RowCount; m++)
  {
    for(int n = 0; n < ResultStringGrid->ColCount; n++)
    {
      ResultStringGrid->Cells[n][m] = "";
    }
  }

  ResultStringGrid->Cells[1][0] = "SD1";
  ResultStringGrid->Cells[2][0] = "SD2";

  if(!Default)
  {
    int dataIndex = 1;
    for(int m = MAX_MOTOR_SPEED - 1; m >= 0; m--)
    {
      if(SD1LineScanFrequencyTable[m] || SD2LineScanFrequencyTable[m])
      {
        int targetSpeed = MachineSetupData.ServoMotorData[0].BaseSpeed * 100 / m;

        int SD1Diff, SD2Diff;

        SD1Diff = SD1LineScanFrequencyTable[m] - LSFModelingTable[m];
        SD2Diff = SD2LineScanFrequencyTable[m] - LSFModelingTable[m];

        ResultStringGrid->Cells[0][dataIndex] = IntToStr(targetSpeed);
        ResultStringGrid->Cells[1][dataIndex] = IntToStr(SD1LineScanFrequencyTable[m]) + "(" + IntToStr(SD1Diff) + ")";
        ResultStringGrid->Cells[2][dataIndex] = IntToStr(SD2LineScanFrequencyTable[m]) + "(" + IntToStr(SD2Diff) + ")";
        dataIndex++;
        ResultStringGrid->RowCount++;
      }
    }
  }
}
//---------------------------------------------------------------------------
void __fastcall TThreeDLineScanFrequencyStudyForm::ResultStringGridSelectCell(
      TObject *Sender, int ACol, int ARow, bool &CanSelect)
{
  if(globalLSFMaxValue == 0)
  {
    return;
  }

  try
  {
    int XSpace, YSpace;
    int DiskSpeed;

    if(ResultStringGrid->Cells[0][ARow] == "")
    {
      return;
    }
    
    DiskSpeed = StrToInt(ResultStringGrid->Cells[0][ARow]);

    if(DiskSpeed == 0)
    {
      return;
    }

    int targetSpeed = MachineSetupData.ServoMotorData[0].BaseSpeed * 100 / DiskSpeed;
    int ImageHeight = 490;

    XSpace = YSpace = 20;

    int navigatorX = XSpace + DiskSpeed * 2;
    int navigatorY = YSpace + LSFModelingTable[targetSpeed] * ImageHeight / globalLSFMaxValue;

    ViewStudyResult(true, navigatorX, navigatorY);
  }
  catch(...)
  {

  }
}
//---------------------------------------------------------------------------


void __fastcall TThreeDLineScanFrequencyStudyForm::ApplyLSFModelBtnClick(
      TObject *Sender)
{ 
  if (MessageDlgFA(THREED_LSF_STUDY_FORM_TEXT_06, mtConfirmation, TMsgDlgButtons() << mbOK << mbCancel) == mrOk)
  {
    memcpy(SD1LineScanFrequencyTable, LSFModelingTable, sizeof(int) * MAX_MOTOR_SPEED);
    memcpy(SD2LineScanFrequencyTable, LSFModelingTable, sizeof(int) * MAX_MOTOR_SPEED);

    SetTableValue(Disk1LSFTableStringGrid, SD1LineScanFrequencyTable, true);
    SetTableValue(Disk2LSFTableStringGrid, SD2LineScanFrequencyTable, true);

    AnsiString fileName = ProgramPath.Env + "\\LSFStudyResultTable.lsf";
    WriteDiskLSFTable(fileName);

    SetResultTable(true);
    SetBlankImage(LSFResultImage, 1);
    ApplyLSFModelBtn->Enabled = false;

    MessageDlgFA("Complete", mtConfirmation, TMsgDlgButtons() << mbOK);
  }
  else
  {
    MessageDlgFA("Cancel", mtConfirmation, TMsgDlgButtons() << mbOK);
  }
}
//---------------------------------------------------------------------------

void __fastcall TThreeDLineScanFrequencyStudyForm::StudyResultTestCheckBoxClick(
      TObject *Sender)
{
  if(StudyResultTestCheckBox->Checked)
  {
    GroupBox7->Visible = false;
  }
  else
  {
    GroupBox7->Visible = true;
  }
}
//---------------------------------------------------------------------------

void __fastcall TThreeDLineScanFrequencyStudyForm::FormActivate(
      TObject *Sender)
{
  SetScreenPosition(this);  
}
//---------------------------------------------------------------------------

