#include <vcl.h>
#include <fstream> 
#include <iostream.h>
#include <math.h>
#include <dir.h>
#pragma hdrstop

#include "Main.h"
#include "Connect.h"
#include "About.h"
#include "Colours.h"
#include "Settings.h"

#pragma package(smart_init)
#pragma link "FloatEdit"
#pragma link "ThemeMgr"
#pragma link "FloatEdit"
#pragma resource "*.dfm"
TMainForm *MainForm;

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

__fastcall TMainForm::TMainForm(TComponent* Owner)
        : TForm(Owner)
{
}

//-------------------------- Создание формы: --------------------------------

void __fastcall TMainForm::FormCreate(TObject *Sender)
{ int i;
  //общие параметры:
  Caption = MakeCaption();
  DecimalSeparator = '.';            //десятичный разделитель
  FFileName = "";
  DragAcceptFiles(Handle, 1);        //разрешение drag-n-drop
  //установка DoubleBuffered для компонентов:
  MainForm->DoubleBuffered = 1;
  UnderLogPanel->DoubleBuffered = 1;
  LogPanel->DoubleBuffered = 1;
  CtrlPanel->DoubleBuffered = 1;
  TrsPanel->DoubleBuffered = 1;
  ToolBar->DoubleBuffered = 1;
  TlPanel->DoubleBuffered = 1;
  StatusBar->DoubleBuffered = 1;
  //настройка связи с устройством:
  WakePort = new TWakePort();
  acDisconnectExecute(NULL);
  //инициализация переменных:
  Bm = new Graphics::TBitmap;
  FCursor = 0;
  FCursorX = 0;    //обнуление координаты курсора
  SetDefColours(); //установка цветов по умолчанию
  FModified = 0;
  //чтение конфигурации:
  Lock = 1;
  ReadConfig();
  Tagging();
  Lock = 0;
  //текущий режим:
  FMode = MD_STOP;                    //запись графика остановлена
  Disconnect();
  Clear();
  ClearScale();
  Timer1->Enabled = 1;                //разрешение таймера
}

//----------------------- Запрос закрытия формы: ----------------------------

void __fastcall TMainForm::FormCloseQuery(TObject *Sender, bool &CanClose)
{
	int Ans = IDNO;
  if(FModified)
   Ans = MessageDlg("Data modified. Save changes?", mtConfirmation,
                    TMsgDlgButtons() << mbYes << mbNo << mbCancel, 0);
  if(Ans == IDYES) acSaveasExecute(NULL);
  if(Ans == IDCANCEL) CanClose = 0;
}

//-------------------------- Закрытие формы: --------------------------------

void __fastcall TMainForm::FormClose(TObject *Sender, TCloseAction &Action)
{
  SaveConfig();
  Disconnect();
  delete Bm;
}

//------------------ Принятие файла по Drag-n-drop: -------------------------

void __fastcall TMainForm::WmDropFiles(TWMDropFiles &Message)
{
	HDROP drop_handle = (HDROP)Message.Drop;
	char fName[MAXPATH];
	DragQueryFile(drop_handle, 0, fName, MAXPATH);
	ReadData(fName);
	DragFinish(drop_handle);
}

//-------------------- Формирование заголовка окна: -------------------------

AnsiString __fastcall TMainForm::MakeCaption(void)
{
  DecimalSeparator = '.';
  AnsiString s = SOFTNAME;
  s = s + " " + FloatToStrF(VERSION, ffFixed, 3, 2);
  return(s);
}

//-------------------- Установка цветов по умолчанию: -----------------------

void __fastcall TMainForm::SetDefColours(void)
{
  Col_Bg = (TColor)16777215; //RGB(255,255,255);
  Col_Ax = (TColor)13948116; //RGB(172,172,172);
  Col_Tx = (TColor)4737096;  //RGB(0,0,0);
  Col_Cr = (TColor)0;        //RGB(0,0,0);
  Col_G1 = (TColor)32768;    //RGB(255,0,0);
  Col_G2 = (TColor)255;      //RGB(0,128,0);
  Col_G3 = (TColor)4227327;  //RGB(0,0,255);
  Col_G4 = (TColor)16711680; //RGB(255,92,92);
  Col_G5 = (TColor)4259584;  //RGB(0,255,64);
  Col_G6 = (TColor)12615935; //RGB(255,128,192);
}

//------------------------- Чтение INI-файла: -------------------------------

void __fastcall TMainForm::ReadConfig(void)
{
  TMemIniFile *ini;
  ini = new TMemIniFile(ChangeFileExt(Application->ExeName,".ini" ));
  Left = ini->ReadInteger("General","Left",Left);
  Top = ini->ReadInteger("General","Top",Top);
  Width = ini->ReadInteger("General","Width",Width);
  Height = ini->ReadInteger("General","Height",Height);
  WindowState = TWindowState(ini->ReadInteger("General", "WindowState", WindowState));
  FDataDir = ini->ReadString("General","DataDir",ExtractFilePath(ParamStr(0)));
  FPortNumber = ini->ReadInteger("General","Port",0);
  cbGr1->Checked = ini->ReadBool("General","ShowG1",1);
  cbGr2->Checked = ini->ReadBool("General","ShowG2",1);
  cbGr3->Checked = ini->ReadBool("General","ShowG3",0);
  cbGr4->Checked = ini->ReadBool("General","ShowG4",0);
  cbGr5->Checked = ini->ReadBool("General","ShowG5",0);
  cbGr6->Checked = ini->ReadBool("General","ShowG6",0);
  FAutoConnect = ini->ReadBool("Settings","AutoConnect",1);
  FAutoLoad = ini->ReadBool("Settings","AutoLoad",1);
  FSample = ini->ReadInteger("Settings","Sample",20);
  FAutoScroll = ini->ReadBool("Settings","AutoScroll",1);
  Col_Bg = (TColor)ini->ReadInteger("Colours","Background",Col_Bg);
  Col_Ax = (TColor)ini->ReadInteger("Colours","Axis",Col_Ax);
  Col_Tx = (TColor)ini->ReadInteger("Colours","Text",Col_Tx);
  Col_Cr = (TColor)ini->ReadInteger("Colours","Cursor",Col_Cr);
  Col_G1 = (TColor)ini->ReadInteger("Colours","Graph1",Col_G1);
  Col_G2 = (TColor)ini->ReadInteger("Colours","Graph2",Col_G2);
  Col_G3 = (TColor)ini->ReadInteger("Colours","Graph3",Col_G3);
  Col_G4 = (TColor)ini->ReadInteger("Colours","Graph4",Col_G4);
  Col_G5 = (TColor)ini->ReadInteger("Colours","Graph5",Col_G5);
  Col_G6 = (TColor)ini->ReadInteger("Colours","Graph6",Col_G6);
  delete ini;
}

//----------------------- Сохранение INI-файла: -----------------------------

void __fastcall TMainForm::SaveConfig(void)
{
  TMemIniFile *ini;
  ini = new TMemIniFile(ChangeFileExt(Application->ExeName,".ini" ));
  ini->WriteInteger("General", "WindowState", WindowState);
  if (WindowState != wsMaximized)
   { ini->WriteInteger("General","Left",Left);
     ini->WriteInteger("General","Top",Top);
     ini->WriteInteger("General","Width",Width);
     ini->WriteInteger("General","Height",Height);
   }
  ini->WriteString("General","DataDir",FDataDir);
  ini->WriteInteger("General","Port",FPortNumber);
  ini->WriteBool("General","ShowG1",cbGr1->Checked);
  ini->WriteBool("General","ShowG2",cbGr2->Checked);
  ini->WriteBool("General","ShowG3",cbGr3->Checked);
  ini->WriteBool("General","ShowG4",cbGr4->Checked);
  ini->WriteBool("General","ShowG5",cbGr5->Checked);
  ini->WriteBool("General","ShowG6",cbGr6->Checked);
  ini->WriteBool("Settings","AutoConnect",FAutoConnect);
  ini->WriteBool("Settings","AutoRead",FAutoLoad);
  ini->WriteInteger("Settings","Sample",FSample);
  ini->WriteBool("Settings","AutoScroll",FAutoScroll);
  ini->WriteInteger("Colours","Background",Col_Bg);
  ini->WriteInteger("Colours","Axis",Col_Ax);
  ini->WriteInteger("Colours","Text",Col_Tx);
  ini->WriteInteger("Colours","Cursor",Col_Cr);
  ini->WriteInteger("Colours","Graph1",Col_G1);
  ini->WriteInteger("Colours","Graph2",Col_G2);
  ini->WriteInteger("Colours","Graph3",Col_G3);
  ini->WriteInteger("Colours","Graph4",Col_G4);
  ini->WriteInteger("Colours","Graph5",Col_G5);
  ini->WriteInteger("Colours","Graph6",Col_G6);
  try { ini->UpdateFile(); } catch (Exception &E)
   { MessageDlg(E.Message, mtError, TMsgDlgButtons() << mbOK, 0); }
  delete ini;
}

//------------------------ Чтение файла данных: -----------------------------

void __fastcall TMainForm::ReadData(AnsiString Name)
{
  //сохранение пути и имени файла:
  FDataDir = ExtractFilePath(Name);
  FFileName = ExtractFileName(Name);
  bool result = 1;
  TStringList *in = new TStringList();
  TStringList *line = new TStringList();
  TCursor Save_Cursor = Screen->Cursor;
  try
  {
    Screen->Cursor = crHourGlass;
    try
    {
      //чтение файла в StringList:
      in->LoadFromFile(Name);
      //если первая строка файла не содержит текста "Time",
      //то ошибка формата файла:
      if(!in->Strings[0].AnsiPos("Time"))
        throw EInOutError("Bad file format");
      acStopExecute(NULL); //остановка накопления массивов
      Clear(); //очистка массивов
      ClearScale(); //очистка шкал
      //восстановление набора графиков, сохраненных в файле:
      cbGr1->Checked = in->Strings[0].AnsiPos("G1");
      cbGr2->Checked = in->Strings[0].AnsiPos("G2");
      cbGr3->Checked = in->Strings[0].AnsiPos("G3");
      cbGr4->Checked = in->Strings[0].AnsiPos("G4");
      cbGr5->Checked = in->Strings[0].AnsiPos("G5");
      cbGr6->Checked = in->Strings[0].AnsiPos("G6");
      //подготовка к парсингу строк:
      line->Delimiter = '\t';
      for(int i = 1; i < in->Count; i++)
      {
        //построчная загрузка файла во второй StringList
        //в виде DelimitedText:
        line->DelimitedText = in->Strings[i];
        int j = 1; //первая колонка - время, пропускаем
        //FSample берем из первой колонки второй строки:
        if(i == 2) FSample = StrToInt(line->Strings[0]);
        //преобразуем данные колонок в int и сохраняем в массивах:
        Data1[FPointer] = StrToFloat(line->Strings[j++]);
        Data2[FPointer] = StrToFloat(line->Strings[j++]);
        Data3[FPointer] = StrToFloat(line->Strings[j++]);
        Data4[FPointer] = StrToFloat(line->Strings[j++]);
        Data5[FPointer] = StrToFloat(line->Strings[j++]);
        if(line->Count > j)
          Data6[FPointer] = StrToFloat(line->Strings[j]);
        FPointer++;
      }
    }
    catch (Exception &E)
    {
      MessageDlg("Bad file format.", mtError, TMsgDlgButtons() << mbOK, 0);
      result = 0;
    }
  }
  __finally
  {
    Screen->Cursor = Save_Cursor;
    delete in;
    delete line;
    //перерисовка графиков:
    RedrawAll();
  }
  if(result) //если файл был открыт успешно,
  {
    //обновление заголовка приложения:
    Caption = MakeCaption();
    Caption = Caption + " - " + FFileName;
  }
}

//---------------------- Сохранение файла данных: --------------------------

void __fastcall TMainForm::SaveData(AnsiString Name)
{
  //сохранение пути и имени файла:
  FDataDir = ExtractFilePath(Name);
  FFileName = ExtractFileName(Name);
  //сохранение данных в файл:
  TStringList *out = new TStringList();
  TCursor Save_Cursor = Screen->Cursor;
  bool not_saved = 0;
  try
  {
    Screen->Cursor = crHourGlass;
    //формирование строки заголовка:
    AnsiString str = "Time";
    if(cbGr1->Checked) str = str + "\t" + "G1";
    if(cbGr2->Checked) str = str + "\t" + "G2";
    if(cbGr3->Checked) str = str + "\t" + "G3";
    if(cbGr4->Checked) str = str + "\t" + "G4";
    if(cbGr5->Checked) str = str + "\t" + "G5";
    if(cbGr6->Checked) str = str + "\t" + "G6";
    out->Add(str);
    //сохранение массивов:
    for(int i = 0; i < FPointer; i++)
    {
      str = IntToStr(i * FSample);
      str = str + "\t" + FloatToStr(Data1[i]);
      str = str + "\t" + FloatToStr(Data2[i]);
      str = str + "\t" + FloatToStr(Data3[i]);
      str = str + "\t" + FloatToStr(Data4[i]);
      str = str + "\t" + FloatToStr(Data5[i]);
      str = str + "\t" + FloatToStr(Data6[i]);
      out->Add(str);
    }
    try
    {
      out->SaveToFile(Name);
    }
    catch (Exception &E)
    {
      MessageDlg("Unable to save file.", mtError, TMsgDlgButtons() << mbOK, 0);
      not_saved = FModified;
    }
  }
  __finally
  {
    Screen->Cursor = Save_Cursor;
    delete out;
  }
  //обновление заголовка приложения:
  Caption = MakeCaption();
  Caption = Caption + " - " + FFileName;
  FModified = not_saved;
}

//---------------------------------------------------------------------------
//------------------------------ Menus: -------------------------------------
//---------------------------------------------------------------------------

//----------------------------- Menu New: -----------------------------------

void __fastcall TMainForm::acNewExecute(TObject *Sender)
{
  Clear();
  RedrawAll();
  FFileName = "";
  Caption = MakeCaption();
  FModified = 0;
}

//----------------------------- Menu Open: ----------------------------------

void __fastcall TMainForm::acOpenExecute(TObject *Sender)
{
  //установка сепаратора, иначе после "засыпания" Windows
  //восстанавливается системный по умолчанию
  DecimalSeparator = '.';
  //заполнение полей диалога:
  OpenDialog->Title = "Open Data File";
  OpenDialog->InitialDir = FDataDir;
  OpenDialog->FileName = "";
  OpenDialog->Filter = "Data file (*.dat)|*.dat|All files (*.*)|*.*";
  OpenDialog->DefaultExt = "dat";
  //вызов диалога "Open...":
  if(!OpenDialog->Execute()) return;
  //перерисовка формы под окном диалога:
  Refresh();
  //чтение файла:
  ReadData(OpenDialog->FileName);
}

//--------------------------- Menu Save As: ---------------------------------

void __fastcall TMainForm::acSaveasExecute(TObject *Sender)
{
  //установка сепаратора, иначе после "засыпания" Windows
  //восстанавливается системный по умолчанию
  DecimalSeparator = '.';
  //заполнение полей диалога:
  SaveDialog->Title = "Save Data File";
  SaveDialog->InitialDir = FDataDir;
  SaveDialog->FileName = FFileName;
  SaveDialog->Filter = "Data file (*.dat)|*.dat|All files (*.*)|*.*";
  SaveDialog->DefaultExt = "dat";
  //вызов диалога "Save As...":
  if(!SaveDialog->Execute()) return;
  //перерисовка формы под окном диалога:
  Refresh();
  //сохранение файла:
  SaveData(SaveDialog->FileName);
}

//---------------------------- Menu Exit: -----------------------------------

void __fastcall TMainForm::acExitExecute(TObject *Sender)
{
  Close();
}

//----------------------------- Connect: ------------------------------------

void __fastcall TMainForm::acConnectExecute(TObject *Sender)
{
  try
  {
    Timer1->Enabled = 0;
    if(!ConnectForm) ConnectForm = new TConnectForm(Application, &FPortNumber);
    if(ConnectForm->ShowModal() == mrOk)
    {
      Disconnect();
      Connect();
    }
  }
  __finally { Timer1->Enabled = 1; }
}

void __fastcall TMainForm::Connect(void)
{
  try
  {
    WakePort->Open(FPortNumber);
    FPortName = WakePort->PortName;
    FDeviceName = WakePort->GetInfo(0);
    AnsiString name = DEVICE_NAME;
    FConnected = FDeviceName.SubString(1, name.Length()) == name;
    if(!FConnected) throw EInOutError("Unknown device");
    StatusBar->Panels->Items[0]->Text = " ON LINE: " +
      FDeviceName + " (" + FPortName + ") ";
    Timer1->Interval = FSample;
    //автоматическое чтение параметров:
    if(FAutoLoad) acReadExecute(NULL);
  }
  catch(const EInOutError &e)
  {
    Disconnect();
  }
}

//---------------------------- Disconnect: ----------------------------------

void __fastcall TMainForm::acDisconnectExecute(TObject *Sender)
{
  Timer1->Enabled = 0;
  FPortNumber = 0;
  Disconnect();
}

void __fastcall TMainForm::Disconnect(void)
{
  WakePort->Close();
  Timer1->Interval = 500;
  FConnected = 0;
  FPortName = "";
  FDeviceName = "";
  if(FPortNumber) StatusBar->Panels->Items[0]->Text = " CONNECTING... ";
    else StatusBar->Panels->Items[0]->Text = " OFF LINE ";
}

//---------------------------- Read Params: ---------------------------------

void __fastcall TMainForm::acReadExecute(TObject *Sender)
{
  TCursor Save_Cursor = Screen->Cursor;
  try
  {
    Screen->Cursor = crHourGlass;
    if(FConnected)
    {
      GetParams();
      for(int m = 0; m < SPOOL_MODES; m++)
        GetTension(m);
    }
  }
  __finally { Screen->Cursor = Save_Cursor; }
}

//--------------------------- Write Params: ---------------------------------

void __fastcall TMainForm::acWriteExecute(TObject *Sender)
{
  TCursor Save_Cursor = Screen->Cursor;
  try
  {
    Screen->Cursor = crHourGlass;
    if(FConnected)
    {
      SetParams();
      for(int m = 0; m < SPOOL_MODES; m++)
        SetTension(m);
    }
  }
  __finally { Screen->Cursor = Save_Cursor; }
}

//---------------------------- Open Params: ---------------------------------

void __fastcall TMainForm::acParOpenExecute(TObject *Sender)
{
  //установка сепаратора, иначе после "засыпания" Windows
  //восстанавливается системный по умолчанию
  DecimalSeparator = '.';
  //заполнение полей диалога:
  OpenDialog->Title = "Open Params File";
  OpenDialog->InitialDir = FDataDir;
  OpenDialog->FileName = "";
  OpenDialog->Filter = "Params file (*.par)|*.par|All files (*.*)|*.*";
  OpenDialog->DefaultExt = "par";
  //вызов диалога "Open Params...":
  if(!OpenDialog->Execute()) return;
  //сохранение пути:
  FDataDir = ExtractFilePath(OpenDialog->FileName);
  //чтение файла параметров:
  TMemIniFile *ini;
  ini = new TMemIniFile(OpenDialog->FileName);
  Lock = 1;
  feCapStart->Value = ini->ReadInteger("Capstan", "Startup", 2000);
  feEmForce->Value  = ini->ReadInteger("Solenoids", "Startup", 1000);
  feTau->Value      = ini->ReadInteger("Tape End Sensor", "Tau", 200);

  feP1->Value = ini->ReadInteger("Spool", "Kp1", 100);
  feI1->Value = ini->ReadInteger("Spool", "Ki1", 40);
  feD1->Value = ini->ReadInteger("Spool", "Kd1", 45);

  feP2->Value = ini->ReadInteger("Spool", "Kp2", 100);
  feI2->Value = ini->ReadInteger("Spool", "Ki2", 40);
  feD2->Value = ini->ReadInteger("Spool", "Kd2", 45);

  feTbrake1->Value = ini->ReadFloat("Spool", "Brake M1", 25.0);
  feTbrake2->Value = ini->ReadFloat("Spool", "Brake M2", 25.0);
  feTplayF1->Value = ini->ReadFloat("Spool", "PlayF M1", 25.0);
  feTplayF2->Value = ini->ReadFloat("Spool", "PlayF M2", 25.0);
  feTplayR1->Value = ini->ReadFloat("Spool", "PlayR M1", 25.0);
  feTplayR2->Value = ini->ReadFloat("Spool", "PlayR M2", 25.0);
  feTffd1->Value = ini->ReadFloat("Spool", "FFD M1", 25.0);
  feTffd2->Value = ini->ReadFloat("Spool", "FFD M2", 100.0);
  feTrew1->Value = ini->ReadFloat("Spool", "REW M1", 100.0);
  feTrew2->Value = ini->ReadFloat("Spool", "REW M2", 25.0);
  feTaffd1->Value = ini->ReadFloat("Spool", "AFFD M1", 15.0);
  feTaffd2->Value = ini->ReadFloat("Spool", "AFFD M2", 30.0);
  feTarew1->Value = ini->ReadFloat("Spool", "AREW M1", 30.0);
  feTarew2->Value = ini->ReadFloat("Spool", "AREW M2", 15.0);
  feTmin1->Value = ini->ReadFloat("Spool", "Autostop M1", 8.0);
  feTmin2->Value = ini->ReadFloat("Spool", "Autostop M2", 8.0);

  feAsPreDel->Value = ini->ReadInteger("Autostop", "Pre-Tension Delay", 800);
  feAsTenDel->Value = ini->ReadInteger("Autostop", "Low-Tension Delay", 1000);
  feAsBrkDel->Value = ini->ReadInteger("Autostop", "Motor Brake Delay", 5000);
  cbAsPreten->Checked = ini->ReadBool("Autostop", "Pre-Tension AS", 1);
  cbAsLowten->Checked = ini->ReadBool("Autostop", "Low-Tension AS", 1);
  cbAsMove->Checked   = ini->ReadBool("Autostop", "Move Sensor AS", 1);
  cbAsBrake->Checked  = ini->ReadBool("Autostop", "Motor Brake AS", 1);

  cbOpPauseBlink->Checked   = ini->ReadBool("Options", "Pause Blink", 0);
  cbOpRecOnPause->Checked   = ini->ReadBool("Options", "Rec Auto Pause", 0);
  cbOpShowDir->Checked      = ini->ReadBool("Options", "Show Direction", 1);
  cbOpArchive->Checked      = ini->ReadBool("Options", "Archive Wind", 1);
  cbOpRollCue->Checked      = ini->ReadBool("Options", "Roll-Back Cue", 1);
  cbOpWindCue->Checked      = ini->ReadBool("Options", "Arch. Wind Cue", 1);
  cbOpNoSound->Checked      = ini->ReadBool("Options", "Beeper Disable", 0);
  cbOpAutoReverse->Checked  = ini->ReadBool("Options", "Auto Reverse", 1);
  cbOpProg->Checked         = ini->ReadBool("Options", "Prog. Enable", 1);
  cbOpProgBlink->Checked    = ini->ReadBool("Options", "Prog. Blink", 0);
  cbOpMotorBrk->Checked     = ini->ReadBool("Options", "Motor Braking", 1);
  cbOpMutePause->Checked    = ini->ReadBool("Options", "Mute Pause", 0);
  cbOpPlayRecEn->Checked    = ini->ReadBool("Options", "Play Rec Enable", 0);
  cbOpRecPlayEn->Checked    = ini->ReadBool("Options", "Rec Play Enable", 0);
  cbOpPlayExPause->Checked  = ini->ReadBool("Options", "Play Exits Pause", 1);
  Lock = 0;
  delete ini;
}

//--------------------------- Save Params: ----------------------------------

void __fastcall TMainForm::acParSaveExecute(TObject *Sender)
{
  //установка сепаратора, иначе после "засыпания" Windows
  //восстанавливается системный по умолчанию
  DecimalSeparator = '.';
  //заполнение полей диалога:
  SaveDialog->Title = "Save Params File";
  SaveDialog->InitialDir = FDataDir;
  SaveDialog->FileName = "Params1";
  SaveDialog->Filter = "Params file (*.par)|*.par|All files (*.*)|*.*";
  SaveDialog->DefaultExt = "par";
  //вызов диалога "Save Params...":
  if(!SaveDialog->Execute()) return;
  //сохранение пути:
  FDataDir = ExtractFilePath(SaveDialog->FileName);
  //сохранение файла параметров:
  TMemIniFile *ini;
  ini = new TMemIniFile(SaveDialog->FileName);
  ini->WriteInteger("Capstan", "Startup", feCapStart->Value);
  ini->WriteInteger("Solenoids", "Startup", feEmForce->Value);
  ini->WriteInteger("Tape End Sensor", "Tau", feTau->Value);

  ini->WriteInteger("Spool", "Kp1", feP1->Value);
  ini->WriteInteger("Spool", "Ki1", feI1->Value);
  ini->WriteInteger("Spool", "Kd1", feD1->Value);

  ini->WriteInteger("Spool", "Kp2", feP2->Value);
  ini->WriteInteger("Spool", "Ki2", feI2->Value);
  ini->WriteInteger("Spool", "Kd2", feD2->Value);

  ini->WriteFloat("Spool", "Brake M1", feTbrake1->Value);
  ini->WriteFloat("Spool", "Brake M2", feTbrake2->Value);
  ini->WriteFloat("Spool", "PlayF M1", feTplayF1->Value);
  ini->WriteFloat("Spool", "PlayF M2", feTplayF2->Value);
  ini->WriteFloat("Spool", "PlayR M1", feTplayR1->Value);
  ini->WriteFloat("Spool", "PlayR M2", feTplayR2->Value);
  ini->WriteFloat("Spool", "FFD M1", feTffd1->Value);
  ini->WriteFloat("Spool", "FFD M2", feTffd2->Value);
  ini->WriteFloat("Spool", "REW M1", feTrew1->Value);
  ini->WriteFloat("Spool", "REW M2", feTrew2->Value);
  ini->WriteFloat("Spool", "AFFD M1", feTaffd1->Value);
  ini->WriteFloat("Spool", "AFFD M2", feTaffd2->Value);
  ini->WriteFloat("Spool", "AREW M1", feTarew1->Value);
  ini->WriteFloat("Spool", "AREW M2", feTarew2->Value);
  ini->WriteFloat("Spool", "Autostop M1", feTmin1->Value);
  ini->WriteFloat("Spool", "Autostop M2", feTmin2->Value);

  ini->WriteInteger("Autostop", "Pre-Tension Delay", feAsPreDel->Value);
  ini->WriteInteger("Autostop", "Low-Tension Delay", feAsTenDel->Value);
  ini->WriteInteger("Autostop", "Motor Brake Delay", feAsBrkDel->Value);
  ini->WriteBool("Autostop", "Pre-Tension AS", cbAsPreten->Checked);
  ini->WriteBool("Autostop", "Low-Tension AS", cbAsLowten->Checked);
  ini->WriteBool("Autostop", "Move Sensor AS", cbAsMove->Checked);
  ini->WriteBool("Autostop", "Motor Brake AS", cbAsBrake->Checked);

  ini->WriteBool("Options", "Pause Blink", cbOpPauseBlink->Checked);
  ini->WriteBool("Options", "Rec Auto Pause", cbOpRecOnPause->Checked);
  ini->WriteBool("Options", "Show Direction", cbOpShowDir->Checked);
  ini->WriteBool("Options", "Archive Wind", cbOpArchive->Checked);
  ini->WriteBool("Options", "Roll-Back Cue", cbOpRollCue->Checked);
  ini->WriteBool("Options", "Arch. Wind Cue", cbOpWindCue->Checked);
  ini->WriteBool("Options", "Beeper Disable", cbOpNoSound->Checked);
  ini->WriteBool("Options", "Auto Reverse", cbOpAutoReverse->Checked);
  ini->WriteBool("Options", "Prog. Enable", cbOpProg->Checked);
  ini->WriteBool("Options", "Prog. Blink", cbOpProgBlink->Checked);
  ini->WriteBool("Options", "Motor Braking", cbOpMotorBrk->Checked);
  ini->WriteBool("Options", "Mute Pause", cbOpMutePause->Checked);
  ini->WriteBool("Options", "Play Rec Enable", cbOpPlayRecEn->Checked);
  ini->WriteBool("Options", "Rec Play Enable", cbOpRecPlayEn->Checked);
  ini->WriteBool("Options", "Play Exits Pause", cbOpPlayExPause->Checked);
  try { ini->UpdateFile(); } catch (Exception &E)
   { MessageDlg(E.Message, mtError, TMsgDlgButtons() << mbOK, 0); }
  delete ini;
}

//-------------------------- Save to EEPROM: --------------------------------

void __fastcall TMainForm::acEepromExecute(TObject *Sender)
{
  acWriteExecute(NULL);
  WakePort->EESave();
}

//--------------------------- Load Defaults: --------------------------------

void __fastcall TMainForm::acDefaultExecute(TObject *Sender)
{
  int Ans = MessageDlg("Are you sure to load the default values?",
              mtConfirmation, TMsgDlgButtons() << mbYes << mbNo, 0);
  if(Ans == IDYES)
  {
    WakePort->Default();
    acReadExecute(NULL);
    acWriteExecute(NULL);
  }
}

//---------------------------- Menu Start: ----------------------------------

void __fastcall TMainForm::acStartExecute(TObject *Sender)
{
  if(!acStart->Checked)
  {
    acStart->Checked = 1;
    acPause->Checked = 0;
    acStop->Checked = 0;
    if(FMode == MD_STOP) acNewExecute(NULL);
    FMode = MD_START;
    FModified = 1;
  }
  else
  {
    acStart->Checked = 0;
    acPause->Checked = 1;
    acStop->Checked = 0;
    FMode = MD_PAUSE;
  }
}

//---------------------------- Menu Pause: ----------------------------------

void __fastcall TMainForm::acPauseExecute(TObject *Sender)
{
  if(acStop->Checked) return;
  if(!acPause->Checked)
  {
    acPause->Checked = 1;
    acStart->Checked = 0;
    acStop->Checked = 0;
    FMode = MD_PAUSE;
  }
  else
  {
    acPause->Checked = 0;
    acStart->Checked = 1;
    acStop->Checked = 0;
    FMode = MD_START;
  }
}

//----------------------------- Menu Stop: ----------------------------------

void __fastcall TMainForm::acStopExecute(TObject *Sender)
{
  acStop->Checked = 0;
  acStart->Checked = 0;
  acPause->Checked = 0;
  FMode = MD_STOP;
}

//----------------------------- Menu Freeze: --------------------------------

void __fastcall TMainForm::acFreezeExecute(TObject *Sender)
{
  acFreeze->Checked = !acFreeze->Checked;
  if(!acFreeze->Checked) RedrawAll();
}

//---------------------------- Menu Test Data: ------------------------------

void __fastcall TMainForm::acTestExecute(TObject *Sender)
{
  double arg = 0;
  for(int i = 0; i < LOGN; i++)
  {
    double s = 1 + sin(arg);
    Data1[i] = 800 + 80 * s;
    Data2[i] = 600 + 70 * s;
    Data3[i] = 100 * s;
    Data4[i] = 200 * s;
    Data5[i] = 300 * s;
    Data6[i] = 400 * s;
    arg += 0.1;
  }
  FPointer = LOGN - 1;
  LogScrollBar->Position = 0;
  DrawLog(LogPaintBox->Canvas, 0, FPointer);
  RedrawAll();
}

//---------------------------- Menu Cursor: ---------------------------------

void __fastcall TMainForm::acCursorExecute(TObject *Sender)
{
  acCursor->Checked = !acCursor->Checked;
  FCursor = acCursor->Checked;
  DrawLogCur(LogPaintBox->Canvas, FCursor, FCursorX);
}

//----------------------------- Cut Left: -----------------------------------

void __fastcall TMainForm::acCutLExecute(TObject *Sender)
{
  if(FCursor)
  {
    int Xo = LogScrollBar->Position + FCursorX;
    if(Xo < FPointer)
    {
      for(int i = 0; i < FPointer - Xo; i++)
      {
        Data1[i] = Data1[i + Xo]; Data1[i + Xo] = 0;
        Data2[i] = Data2[i + Xo]; Data2[i + Xo] = 0;
        Data3[i] = Data3[i + Xo]; Data3[i + Xo] = 0;
        Data4[i] = Data4[i + Xo]; Data4[i + Xo] = 0;
        Data5[i] = Data5[i + Xo]; Data5[i + Xo] = 0;
        Data6[i] = Data6[i + Xo]; Data6[i + Xo] = 0;
      }
      FPointer = FPointer - Xo;
      FCursorX = 0;
      RedrawAll();
      LogScrollBar->Position = 0;
      FModified = 1;
    }
  }
}

//---------------------------- Cut Right: -----------------------------------

void __fastcall TMainForm::acCutRExecute(TObject *Sender)
{
  if(FCursor)
  {
    int Xo = LogScrollBar->Position + FCursorX;
    if(Xo < FPointer)
    {
      for(int i = Xo + 1; i < FPointer; i++)
      {
        Data1[i] = 0;
        Data2[i] = 0;
        Data3[i] = 0;
        Data4[i] = 0;
        Data5[i] = 0;
        Data6[i] = 0;
      }
      FPointer = Xo + 1;
      RedrawAll();
      FModified = 1;
    }
  }
}

//---------------------------- Menu Settings: -------------------------------

void __fastcall TMainForm::acSettingsExecute(TObject *Sender)
{
  if(!SettingsForm) SettingsForm = new TSettingsForm(Application);
  SettingsForm->ShowModal();
  Timer1->Interval = FSample;
  //автоматическая загрузка параметров:
  if(FAutoLoad) acReadExecute(NULL);
  RedrawAll();
}

//---------------------------- Menu Colours: --------------------------------

void __fastcall TMainForm::acColoursExecute(TObject *Sender)
{
  if(!ColoursForm) ColoursForm = new TColoursForm(Application);
  ColoursForm->ShowModal();
}

//---------------------------- Menu About: ----------------------------------

void __fastcall TMainForm::acAboutExecute(TObject *Sender)
{
  if(!AboutForm) AboutForm = new TAboutForm(Application);
  AboutForm->ShowModal();
}

//-------------------------------- PWM: -------------------------------------
//--------------------------- Вывод графиков: -------------------------------
//---------------------------------------------------------------------------

#define SCALE  100 //шкала по оси Y
#define X_STEP  50 //шаг сетки по оси X
#define Y_DIV   20 //количество делений по оси Y

#define GAPL    32 //зазор слева от сетки
#define GAPR     4 //зазор справа от сетки
#define GAPT    22 //зазор сверху от сетки
#define GAPB    22 //зазор снизу от сетки

//---------------------------- Вывод сетки: ---------------------------------

void __fastcall TMainForm::DrawLogGrid(TCanvas *cv)
{
  //задание цветов:
  TrsPanel->Color = Col_Bg;
  lbTraces->Font->Color = Col_Tx;
  lbGr1->Font->Color = Col_G1;
  lbGr2->Font->Color = Col_G2;
  lbGr3->Font->Color = Col_G3;
  lbGr4->Font->Color = Col_G4;
  lbGr5->Font->Color = Col_G5;
  lbGr6->Font->Color = Col_G6;
  //вычисление диапазонов:
  int DeltaX = LogPaintBox->Width - GAPL - GAPR;
  int DeltaY = LogPaintBox->Height - GAPB - GAPT;
  LogScrollBar->Max = LOGN - DeltaX;
  int Xo = LogScrollBar->Position;
  //очистка:
  cv->Pen->Mode = pmCopy;
  cv->Brush->Color = Col_Bg;
  cv->FillRect(Rect(0, 0, LogPaintBox->Width, LogPaintBox->Height));
  //рисование вертикальных линий сетки:
  cv->Pen->Color = Col_Ax;
  cv->Font->Color = Col_Tx;
  int i = - Xo % X_STEP; if(i < 0) i += X_STEP;
  cv->MoveTo(GAPL, GAPT);
  cv->LineTo(GAPL, GAPT + DeltaY);
  cv->MoveTo(GAPL + DeltaX - 1, GAPT);
  cv->LineTo(GAPL + DeltaX - 1, GAPT + DeltaY);
  AnsiString txt;
  while(i < DeltaX)
  {
    cv->MoveTo(GAPL + i, GAPT);
    cv->LineTo(GAPL + i, GAPT + DeltaY + 3);
    txt = IntToStr((Xo + i) * FSample / 1000);
    int x = GAPL + i - cv->TextWidth(txt) / 2;
    cv->TextOutA(x, GAPT + DeltaY + 5, txt);
    i += X_STEP;
  }
  //cv->TextOutA(GAPL + DeltaX + 14, GAPT + DeltaY + 5, "s");
  //рисование горизонтальных линий сетки:
  cv->Font->Color = Col_Tx;
  txt = "0";
  int h = cv->TextHeight(txt) / 2;
  for(int i = 0; i <= Y_DIV; i++)
  {
    int y = GAPT + (i * (double)DeltaY / Y_DIV + 0.5);
    cv->MoveTo(GAPL - 3, y);
    cv->LineTo(GAPL + DeltaX, y);
    //вывод текста по Y:
    y = y - h;
    txt = IntToStr((Y_DIV - i) * SCALE / Y_DIV);
    int x = GAPL - cv->TextWidth(txt) - 7;
    cv->TextOutA(x, y, txt);
  }
  FCurOn = 0;
  DrawLogCur(cv, FCursor, FCursorX);
}

//--------------- Рисование графика в указанном диапазоне: ------------------

#define RNG1 (SCALE / 1000.0)
#define RNG2 (SCALE / 1000.0)
#define RNG3 (SCALE / 1000.0)
#define RNG4 (SCALE / 1000.0)
#define RNG5 (SCALE / 1000.0)
#define RNG6 (SCALE / 1000.0)

void __fastcall TMainForm::DrawLog(TCanvas *cv, int x1, int x2)
{
  //вычисление диапазонов:
  int DeltaX = LogPaintBox->Width - GAPL - GAPR;
  int DeltaY = LogPaintBox->Height - GAPB - GAPT;
  int Xo = LogScrollBar->Position;
  x1 = x1 - Xo; x2 = x2 - Xo;
  if(x1 < 0) x1 = 0;
  if(x2 > DeltaX - 1) x2 = DeltaX - 1;
  //выключение курсора:
  DrawLogCur(cv, 0, FCursorX);
  //рисование графиков:
  cv->Pen->Mode = pmCopy;
  for(int i = x1; i <= x2; i++)
  {
    if(cbGr1->Checked)
      DrawSegment(cv, Col_G1, Data1, Scale[0] * RNG1, Shift[0], Xo, i, DeltaY);
    if(cbGr2->Checked)
      DrawSegment(cv, Col_G2, Data2, Scale[1] * RNG2, Shift[1], Xo, i, DeltaY);
    if(cbGr3->Checked)
      DrawSegment(cv, Col_G3, Data3, Scale[2] * RNG3, Shift[2], Xo, i, DeltaY);
    if(cbGr4->Checked)
      DrawSegment(cv, Col_G4, Data4, Scale[3] * RNG4, Shift[3], Xo, i, DeltaY);
    if(cbGr5->Checked)
      DrawSegment(cv, Col_G5, Data5, Scale[4] * RNG5, Shift[4], Xo, i, DeltaY);
    if(cbGr6->Checked)
      DrawSegment(cv, Col_G6, Data6, Scale[5] * RNG6, Shift[5], Xo, i, DeltaY);
  }
  //включение курсора:
  DrawLogCur(cv, FCursor, FCursorX);
}

//--------------------- Рисование сегмента графика: -------------------------

void __fastcall TMainForm::DrawSegment(TCanvas *cv, TColor cl, double *data,
                             double range, int shift, int x0, int x, int dy)
{
  cv->Pen->Color = cl;
  //double k = double(dy) / range;
  double k = double(dy) / 100.0 * range;
  int Y2 = int(k * (data[x0 + x] + shift) + 0.5);
  int Y1 = int(k * (data[x0 + x - ((x)? 1 : 0)] + shift) + 0.5);
  if(Y1 > dy) Y1 = dy;
  if(Y2 > dy) Y2 = dy;
  if(Y1 < 0) Y1 = 0;
  if(Y2 < 0) Y2 = 0;
  if(Y2 < Y1) Y2 = Y2 - 1; else Y2 = Y2 + 1;
  cv->MoveTo(GAPL + x, GAPT + dy - Y1);
  cv->LineTo(GAPL + x, GAPT + dy - Y2);
}

//------------------------- Рисование курсора: ------------------------------

void __fastcall TMainForm::DrawLogCur(TCanvas *cv, bool e, int x)
{
  //вычисление диапазонов:
  int DeltaX = LogPaintBox->Width - GAPL - GAPR;
  int DeltaY = LogPaintBox->Height - GAPB - GAPT;
  int Xo = LogScrollBar->Position;
  if (x < 0) x = 0;
  if (x > DeltaX - 1) x = DeltaX - 1;
  cv->Pen->Mode = pmNotXor;
  if(FCurOn)
  {
    //стирание курсора:
    cv->Pen->Color = (Col_Cr ^ ~Col_Bg) & 0xFFFFFF;
    cv->MoveTo(GAPL + FCursorX, GAPT + DeltaY);
    cv->LineTo(GAPL + FCursorX, GAPT - 1);
  }
  if(e)
  {
    int j = x + Xo;
    //рисование курсора:
    cv->Pen->Color = (Col_Cr ^ ~Col_Bg) & 0xFFFFFF;
    cv->MoveTo(GAPL + x, GAPT + DeltaY);
    cv->LineTo(GAPL + x, GAPT - 1);
    //вывод координат Y:
    DecimalSeparator = '.';
    if(cbGr1->Checked)
      StatusBar->Panels->Items[1]->Text =
       "T1 = " + FloatToStrF(Data1[j] / 10,  ffFixed, 3, 1) + "%";
      else StatusBar->Panels->Items[1]->Text = "";
    if(cbGr2->Checked)
      StatusBar->Panels->Items[2]->Text =
       "T2 = " + FloatToStrF(Data2[j] / 10,  ffFixed, 3, 1) + "%";
      else StatusBar->Panels->Items[2]->Text = "";
    if(cbGr3->Checked)
      StatusBar->Panels->Items[3]->Text =
       "M1 = " + FloatToStrF(Data3[j] / 10,  ffFixed, 3, 1) + "%";
      else StatusBar->Panels->Items[3]->Text = "";
    if(cbGr4->Checked)
      StatusBar->Panels->Items[4]->Text =
       "M2 = " + FloatToStrF(Data4[j] / 10,  ffFixed, 3, 1) + "%";
      else StatusBar->Panels->Items[4]->Text = "";
    if(cbGr5->Checked)
      StatusBar->Panels->Items[5]->Text =
       "R1 = " + FloatToStrF(Data5[j] / 10,  ffFixed, 3, 1) + "%";
      else StatusBar->Panels->Items[5]->Text = "";
    if(cbGr6->Checked)
      StatusBar->Panels->Items[6]->Text =
       "R2 = " + FloatToStrF(Data6[j] / 10,  ffFixed, 3, 1) + "%";
      else StatusBar->Panels->Items[6]->Text = "";
    //вывод координаты X:
    StatusBar->Panels->Items[7]->Text = " t = " +
     FloatToStrF(FSample * (j) / 1000.0, ffFixed, 5, 1) + " s ";
  }
  else
  {
    //очистка координат, если курсор выключен:
    StatusBar->Panels->Items[1]->Text = "";
    StatusBar->Panels->Items[2]->Text = "";
    StatusBar->Panels->Items[3]->Text = "";
    StatusBar->Panels->Items[4]->Text = "";
    StatusBar->Panels->Items[5]->Text = "";
    StatusBar->Panels->Items[6]->Text = "";
    StatusBar->Panels->Items[7]->Text = "";
  }
  FCurOn = e;
  FCursorX = x;
}

//-------------------- Включение/отключение графиков: -----------------------

void __fastcall TMainForm::cbGr1Click(TObject *Sender)
{
  RedrawAll();
}

//--------------------------- Скроллирование: -------------------------------

void __fastcall TMainForm::LogScrollBarChange(TObject *Sender)
{
  RedrawAll();
}

//------------------------ Перерисовка графика: -----------------------------

void __fastcall TMainForm::LogPaintBoxPaint(TObject *Sender)
{
  RedrawAll();
}

//---------------------------- Cursor On: -----------------------------------

void __fastcall TMainForm::LogPaintBoxMouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
  if(Shift.Contains(ssLeft))
  {
    acCursor->Checked = 1;
    FCursor = 1;
    DrawLogCur(LogPaintBox->Canvas, FCursor, X - GAPL);
  }
}

//--------------------------- Cursor Move: ----------------------------------

void __fastcall TMainForm::LogPaintBoxMouseMove(TObject *Sender,
      TShiftState Shift, int X, int Y)
{
  if (Shift.Contains(ssLeft))
  {
    //автоматическое скроллирование:
    if(FAutoScroll)
    {
      if(X > LogPaintBox->Width - GAPR)
        LogScrollBar->Position += X - LogPaintBox->Width + GAPR;
      if(X < GAPL)
        LogScrollBar->Position -= GAPL - X;
    }
    DrawLogCur(LogPaintBox->Canvas, FCursor, X - GAPL);
  }
}

//-------------------------- Очистка данных: --------------------------------

void __fastcall TMainForm::Clear(void)
{
  for(int i = 0; i < LOGN; i++)
  {
    Data1[i] = 0;
    Data2[i] = 0;
    Data3[i] = 0;
    Data4[i] = 0;
    Data5[i] = 0;
    Data6[i] = 0;
  }
  LogScrollBar->Position = 0;
  FPointer = 0;
}

//-------------------------- Очистка шкал: ----------------------------------

void __fastcall TMainForm::ClearScale(void)
{
  for(int i = 0; i < GRAPHS; i++)
  {
    Scale[i] = 1;
    BaseShift[i] = 0;
    Shift[i] = 0;
  }
  int index = cbGrs->ItemIndex;
  feScale->Value = Scale[index];
  feShift->Value = Shift[index] / 10.0;
  sbShift->Position = 0;
  sbScale->Position = 990;
}

//------------------------ Перерисовка всего: -------------------------------

void __fastcall TMainForm::RedrawAll(void)
{
  Bm->Height = LogPaintBox->Height;
  Bm->Width = LogPaintBox->Width;
  DrawLogGrid(Bm->Canvas);
  if(FPointer) DrawLog(Bm->Canvas, 0, FPointer - 1);
  LogPaintBox->Canvas->Draw(0, 0, Bm);
  //int index = cbGrs->ItemIndex;
  sbShift->Max = 1000;//LogPaintBox->Height * 2;
  sbShift->Min = -1000; //-LogPaintBox->Height * 2;
}

//-------------------------- Изменение шкалы: -------------------------------

void __fastcall TMainForm::feScaleChanged(TObject *Sender)
{
  if(!Lock)
  {
    Lock = 1;
    int index = cbGrs->ItemIndex;
    Scale[index] = feScale->Value;
    sbScale->Position = sbScale->Max - Scale[index] * 10;
    RedrawAll();
    Lock = 0;
  }
}

void __fastcall TMainForm::sbScaleChange(TObject *Sender)
{
  if(!Lock)
  {
    Lock = 1;
    int index = cbGrs->ItemIndex;
    Scale[index] = (sbScale->Max - sbScale->Position) / 10.0;
    feScale->Value = Scale[index];
    RedrawAll();
    Lock = 0;
  }
}

//------------------------- Изменение смещения: -----------------------------

void __fastcall TMainForm::feShiftChanged(TObject *Sender)
{
  if(!Lock)
  {
    Lock = 1;
    int index = cbGrs->ItemIndex;
    BaseShift[index] = feShift->Value * 10.0;
    Shift[index] = BaseShift[index];
    sbShift->Position = 0;
    RedrawAll();
    Lock = 0;
  }
}

void __fastcall TMainForm::sbShiftChange(TObject *Sender)
{
  if(!Lock)
  {
    Lock = 1;
    int index = cbGrs->ItemIndex;
    Shift[index] = BaseShift[index] - sbShift->Position;
    feShift->Value = Shift[index] / 10.0;
    RedrawAll();
    Lock = 0;
  }
}

//----------------------- Сброс шкалы и смещения: ---------------------------

void __fastcall TMainForm::btResClick(TObject *Sender)
{
  Lock = 1;
  int index = cbGrs->ItemIndex;
  Scale[index] = 1;
  feScale->Value = 1;
  sbScale->Position = sbScale->Max - 10;
  BaseShift[index] = 0;
  Shift[index] = 0;
  feShift->Value = 0;
  sbShift->Position = 0;
  RedrawAll();
  DrawLog(LogPaintBox->Canvas, 0, FPointer);
  Lock = 0;
}

//--------------------------- Выбор графика: --------------------------------

void __fastcall TMainForm::cbGrsChange(TObject *Sender)
{
  Lock = 1;
  int index = cbGrs->ItemIndex;
  feScale->Value = Scale[index];
  feShift->Value = Shift[index] / 10.0;
  sbShift->Position = BaseShift[index] - Shift[index];
  sbScale->Position = sbScale->Max - Scale[index] * 10;
  Lock = 0;
}

//---------------------------------------------------------------------------
//----------------------- Вспомогательные методы: ---------------------------
//---------------------------------------------------------------------------

//---------------------- Назначение тэгов контролам: ------------------------

void __fastcall TMainForm::Tagging(void)
{
  sbRec->Tag = KEY_REC;
  sbPause->Tag = KEY_PAUSE;
  sbPlayR->Tag = KEY_PLAYR;
  sbPlayF->Tag = KEY_PLAYF;
  sbRew->Tag = KEY_REW;
  sbStop->Tag = KEY_STOP;
  sbFfd->Tag = KEY_FFD;
  sbRollBack->Tag = KEY_ROLL;

  feTmin1->Tag    = SPOOL_OFF;    feTmin2->Tag    = SPOOL_OFF;
  feTbrake1->Tag  = SPOOL_BRAKE;  feTbrake2->Tag  = SPOOL_BRAKE;
  feTplayF1->Tag  = SPOOL_PLAYF;  feTplayF2->Tag  = SPOOL_PLAYF;
  feTplayR1->Tag  = SPOOL_PLAYR;  feTplayR2->Tag  = SPOOL_PLAYR;
  feTffd1->Tag    = SPOOL_FFD;    feTffd2->Tag    = SPOOL_FFD;
  feTrew1->Tag    = SPOOL_REW;    feTrew2->Tag    = SPOOL_REW;
  feTaffd1->Tag   = SPOOL_AFFD;   feTaffd2->Tag   = SPOOL_AFFD;
  feTarew1->Tag   = SPOOL_AREW;   feTarew2->Tag   = SPOOL_AREW;
}

//------------------------ Управление светодиодами: -------------------------

void __fastcall TMainForm::SetLeds(leds_t leds)
{
  TColor ColOff  = clGray;   TColor c;
  TColor ColCont = clLime;   TColor ColSlow = clGreen;
  TColor ColNorm = clYellow; TColor ColFast = clSkyBlue;
  c = ColOff;
  if(leds.cont & LED_PLAYF) c = ColCont; if(leds.slow & LED_PLAYF) c = ColSlow;
  if(leds.norm & LED_PLAYF) c = ColNorm; if(leds.fast & LED_PLAYF) c = ColFast;
  shPlayF->Brush->Color = c;
  c = ColOff;
  if(leds.cont & LED_PLAYR) c = ColCont; if(leds.slow & LED_PLAYR) c = ColSlow;
  if(leds.norm & LED_PLAYR) c = ColNorm; if(leds.fast & LED_PLAYR) c = ColFast;
  shPlayR->Brush->Color = c;
  c = ColOff;
  if(leds.cont & LED_PAUSE) c = ColCont; if(leds.slow & LED_PAUSE) c = ColSlow;
  if(leds.norm & LED_PAUSE) c = ColNorm; if(leds.fast & LED_PAUSE) c = ColFast;
  shPause->Brush->Color = c;
  c = ColOff;
  if(leds.cont & LED_FFD) c = ColCont; if(leds.slow & LED_FFD) c = ColSlow;
  if(leds.norm & LED_FFD) c = ColNorm; if(leds.fast & LED_FFD) c = ColFast;
  shFfd->Brush->Color = c;
  c = ColOff;
  if(leds.cont & LED_REW) c = ColCont; if(leds.slow & LED_REW) c = ColSlow;
  if(leds.norm & LED_REW) c = ColNorm; if(leds.fast & LED_REW) c = ColFast;
  shRew->Brush->Color = c;
  c = ColOff;
  if(leds.cont & LED_STOP) c = ColCont; if(leds.slow & LED_STOP) c = ColSlow;
  if(leds.norm & LED_STOP) c = ColNorm; if(leds.fast & LED_STOP) c = ColFast;
  shStop->Brush->Color = c;
  ColCont = clRed; ColSlow = clMaroon;
  c = ColOff;
  if(leds.cont & LED_REC) c = ColCont; if(leds.slow & LED_REC) c = ColSlow;
  if(leds.norm & LED_REC) c = ColNorm; if(leds.fast & LED_REC) c = ColFast;
  shRec->Brush->Color = c;
}

//--------------------- Подсветка подписи натяжения: ------------------------

void __fastcall TMainForm::HighlightLabel(int i)
{
  lbTen1->Font->Style = (i == 1)? TFontStyles() << fsUnderline : TFontStyles();
  lbTen2->Font->Style = (i == 2)? TFontStyles() << fsUnderline : TFontStyles();
  lbTen3->Font->Style = (i == 3)? TFontStyles() << fsUnderline : TFontStyles();
  lbTen4->Font->Style = (i == 4)? TFontStyles() << fsUnderline : TFontStyles();
  lbTen5->Font->Style = (i == 5)? TFontStyles() << fsUnderline : TFontStyles();
  lbTen6->Font->Style = (i == 6)? TFontStyles() << fsUnderline : TFontStyles();
  lbTen7->Font->Style = (i == 7)? TFontStyles() << fsUnderline : TFontStyles();
}

//------------------------- Передача натяжения: -----------------------------

void __fastcall TMainForm::SetTension(int m)
{
  double a, b;
  switch(m)
  {
  case SPOOL_OFF:    a = feTmin1->Value;    b = feTmin2->Value;    break;
  case SPOOL_BRAKE:  a = feTbrake1->Value;  b = feTbrake2->Value;  break;
  case SPOOL_PLAYF:  a = feTplayF1->Value;  b = feTplayF2->Value;  break;
  case SPOOL_PLAYR:  a = feTplayR1->Value;  b = feTplayR2->Value;  break;
  case SPOOL_FFD:    a = feTffd1->Value;    b = feTffd2->Value;    break;
  case SPOOL_REW:    a = feTrew1->Value;    b = feTrew2->Value;    break;
  case SPOOL_AFFD:   a = feTaffd1->Value;   b = feTaffd2->Value;   break;
  case SPOOL_AREW:   a = feTarew1->Value;   b = feTarew2->Value;   break;
  default: return;
  }
  ten_t t; t.m1 = a * 10.0; t.m2 = b * 10.0;
  WakePort->SetTension(m, t);
}

//-------------------------- Чтение натяжения: ------------------------------

void __fastcall TMainForm::GetTension(int m)
{
  ten_t t;
  WakePort->GetTension(m, t);
  double a = t.m1 / 10.0, b = t.m2 / 10.;
  Lock = 1;
  switch(m)
  {
  case SPOOL_OFF:    feTmin1->Value    = a; feTmin2->Value    = b; break;
  case SPOOL_BRAKE:  feTbrake1->Value  = a; feTbrake2->Value  = b; break;
  case SPOOL_PLAYF:  feTplayF1->Value  = a; feTplayF2->Value  = b; break;
  case SPOOL_PLAYR:  feTplayR1->Value  = a; feTplayR2->Value  = b; break;
  case SPOOL_FFD:    feTffd1->Value    = a; feTffd2->Value    = b; break;
  case SPOOL_REW:    feTrew1->Value    = a; feTrew2->Value    = b; break;
  case SPOOL_AFFD:   feTaffd1->Value   = a; feTaffd2->Value   = b; break;
  case SPOOL_AREW:   feTarew1->Value   = a; feTarew2->Value   = b; break;
  default: Lock = 0; return;
  }
  Lock = 0;
}

//-------------------------- Чтение состояния: ------------------------------

void __fastcall TMainForm::GetState(void)
{
  WakePort->GetState(State);
  Lock = 1;
  SetLeds(State.leds);
  cbBreak->Checked = State.b & STB_BRAKE;
  cbPress->Checked = State.b & STB_PRESS;
  cbLift->Checked = State.b & STB_LIFT;
  cbMove->Checked = State.b & STB_MOVE;
  cbTape->Checked = State.b & STB_TAPE;
  cbCapStart->Checked = State.b & STB_RUN;
  cbCapReverse->Checked = State.b & STB_REV;
  cbCapReady->Checked = State.b & STB_READY;
  cbAudioMute->Checked = State.a & AUD_MUTE;
  cbAudioReverse->Checked = State.a & AUD_REV;
  cbAudioRecord->Checked = State.a & AUD_REC;
  if(!cbSpool->DroppedDown) cbSpool->ItemIndex = State.s;
  HighlightLabel(State.s);
  feT1get->Value = State.t1 / 10.0;
  feT2get->Value = State.t2 / 10.0;
  feMot1->Value = State.m1 / 10.0;
  feMot2->Value = State.m2 / 10.0;
  Lock = 0;
}

//------------------------ Передача параметров: -----------------------------

void __fastcall TMainForm::SetParams(void)
{
  params_t p; int op;
  p.kp1 = feP1->Value;
  p.ki1 = feI1->Value;
  p.kd1 = feD1->Value;
  p.kp2 = feP2->Value;
  p.ki2 = feI2->Value;
  p.kd2 = feD2->Value;
  p.CapStart = feCapStart->Value;
  p.EndTau = feTau->Value;
  p.EmForce = feEmForce->Value;
  p.AsBrkDel = feAsBrkDel->Value;
  p.AsPreDel = feAsPreDel->Value;
  p.AsTenDel = feAsTenDel->Value;
  op = 0;
  if(cbAsBrake->Checked)    op |= OPT_BRKASENABLE;
  if(cbAsPreten->Checked)   op |= OPT_PREASENABLE;
  if(cbAsLowten->Checked)   op |= OPT_TENASENABLE;
  if(cbAsMove->Checked)     op |= OPT_MOVASENABLE;
  if(cbOpMotorBrk->Checked) op |= OPT_MOTORBRK;
  p.TrOptions = op;
  op = 0;
  if(cbOpPauseBlink->Checked)   op |= OPT_PAUSELEDBLINK;
  if(cbOpRecOnPause->Checked)   op |= OPT_AUTORECPAUSE;
  if(cbOpShowDir->Checked)      op |= OPT_SHOWDIR;
  if(cbOpArchive->Checked)      op |= OPT_USEARCHIVE;
  if(cbOpRollCue->Checked)      op |= OPT_ROLLCUE;
  if(cbOpWindCue->Checked)      op |= OPT_ENABLECUE;
  if(cbOpNoSound->Checked)      op |= OPT_NOSOUND;
  if(cbOpAutoReverse->Checked)  op |= OPT_AUTOREVERSE;
  if(cbOpProg->Checked)         op |= OPT_ENABLEPROG;
  if(cbOpProgBlink->Checked)    op |= OPT_PROGBLINK;
  if(cbOpMutePause->Checked)    op |= OPT_MUTEPAUSE;
  if(cbOpPlayRecEn->Checked)    op |= OPT_PLAYRECEN;
  if(cbOpRecPlayEn->Checked)    op |= OPT_RECPLAYEN;
  if(cbOpPlayExPause->Checked)  op |= OPT_PLAYEXPAUSE;
  p.CtOptions = op;
  p.sys = feSys->Value;
  WakePort->SetParams(p);
}

//------------------------- Чтение параметров: ------------------------------

void __fastcall TMainForm::GetParams(void)
{
  params_t p;
  WakePort->GetParams(p);
  Lock = 1;
  feP1->Value = p.kp1;
  feI1->Value = p.ki1;
  feD1->Value = p.kd1;
  feP2->Value = p.kp2;
  feI2->Value = p.ki2;
  feD2->Value = p.kd2;
  feCapStart->Value = p.CapStart;
  feTau->Value      = p.EndTau;
  feEmForce->Value  = p.EmForce;
  feAsBrkDel->Value = p.AsBrkDel;
  feAsPreDel->Value = p.AsPreDel;
  feAsTenDel->Value = p.AsTenDel;
  cbAsBrake->Checked        = p.TrOptions & OPT_BRKASENABLE;
  cbAsPreten->Checked       = p.TrOptions & OPT_PREASENABLE;
  cbAsLowten->Checked       = p.TrOptions & OPT_TENASENABLE;
  cbAsMove->Checked         = p.TrOptions & OPT_MOVASENABLE;
  cbOpMotorBrk->Checked     = p.TrOptions & OPT_MOTORBRK;
  cbOpPauseBlink->Checked   = p.CtOptions & OPT_PAUSELEDBLINK;
  cbOpRecOnPause->Checked   = p.CtOptions & OPT_AUTORECPAUSE;
  cbOpShowDir->Checked      = p.CtOptions & OPT_SHOWDIR;
  cbOpArchive->Checked      = p.CtOptions & OPT_USEARCHIVE;
  cbOpRollCue->Checked      = p.CtOptions & OPT_ROLLCUE;
  cbOpWindCue->Checked      = p.CtOptions & OPT_ENABLECUE;
  cbOpNoSound->Checked      = p.CtOptions & OPT_NOSOUND;
  cbOpAutoReverse->Checked  = p.CtOptions & OPT_AUTOREVERSE;
  cbOpProg->Checked         = p.CtOptions & OPT_ENABLEPROG;
  cbOpProgBlink->Checked    = p.CtOptions & OPT_PROGBLINK;
  cbOpMutePause->Checked    = p.CtOptions & OPT_MUTEPAUSE;
  cbOpPlayRecEn->Checked    = p.CtOptions & OPT_PLAYRECEN;
  cbOpRecPlayEn->Checked    = p.CtOptions & OPT_RECPLAYEN;
  cbOpPlayExPause->Checked  = p.CtOptions & OPT_PLAYEXPAUSE;
  feSys->Value = p.sys;
  Lock = 0;
}

//---------------------------------------------------------------------------
//---------------------------- Управление: ----------------------------------
//---------------------------------------------------------------------------

//--------------------------- Main Control: ---------------------------------

//Кнопки управления:

void __fastcall TMainForm::sbStopClick(TObject *Sender)
{
  int m = ((TSpeedButton*)Sender)->Tag;
  WakePort->SetMode(m);
}

//Кнопка "Откат":

void __fastcall TMainForm::sbRollBackClick(TObject *Sender)
{
  WakePort->SetMode(sbRollBack->Down? KEY_ROLL : KEY_UNROLL);
}

//----------------------------- Capstan: ------------------------------------

//Реверс:

void __fastcall TMainForm::cbCapReverseClick(TObject *Sender)
{
  if(!Lock)
  {
    int m = CAP_START;
    if(cbCapReverse->Checked) m |= CAP_REVERSE;
    WakePort->SetCapstan(m);
  }
}

//Время разгона:

void __fastcall TMainForm::feCapStartChanged(TObject *Sender)
{
  if(!Lock)
  {
    SetParams();
  }
}

//---------------------------- Solenoids: -----------------------------------

void __fastcall TMainForm::cbPressClick(TObject *Sender)
{
  if(!Lock)
  {
    int s = 0;
    if(cbBreak->Checked) s |= SOL_BRAKE;
    if(cbPress->Checked) s |= SOL_PRESS;
    if(cbLift->Checked)  s |= SOL_LIFT;
    WakePort->SetSolenoids(s);
  }
}

//------------------------------ Spool: -------------------------------------

//Коэффициенты PID:

void __fastcall TMainForm::feP1Changed(TObject *Sender)
{
  if(!Lock)
  {
    SetParams();
  }
}

//Переключение режима SPOOL:

void __fastcall TMainForm::cbSpoolChange(TObject *Sender)
{
  if(!Lock)
  {
    WakePort->SetSpoolMode(cbSpool->ItemIndex);
    HighlightLabel(cbSpool->ItemIndex);
    if(cbSpool->ItemIndex == SPOOL_OFF)
      WakePort->SetSpool(0, 0);
    feMot1->Enabled = cbSpool->ItemIndex == SPOOL_OFF;
    feMot2->Enabled = cbSpool->ItemIndex == SPOOL_OFF;
  }
}

//Установка натяжения:

void __fastcall TMainForm::feTbrake1Changed(TObject *Sender)
{
  if(!Lock)
  {
    int m = ((TFloatEdit*)Sender)->Tag;
    SetTension(m);
  }
}

//Управление боковыми двигателями:

void __fastcall TMainForm::feMot1Changed(TObject *Sender)
{
  if(!Lock)
  {
    WakePort->SetSpool(feMot1->Value * 10.0, feMot2->Value * 10.0);
  }
}

//---------------------------- Move Sensor: ---------------------------------

//Установка TC:

void __fastcall TMainForm::cbSlowClick(TObject *Sender)
{
  if(!Lock)
  {
    WakePort->SetTC((cbSlow->Checked)? MOVE_SLOW : MOVE_FAST);
  }
}

//----------------------------- Audio: --------------------------------------

void __fastcall TMainForm::cbAudioMuteClick(TObject *Sender)
{
  if(!Lock)
  {
    int a = 0;
    if(cbAudioMute->Checked)    a |= AUD_MUTE;
    if(cbAudioReverse->Checked) a |= AUD_REV;
    if(cbAudioRecord->Checked)  a |= AUD_REC;
    WakePort->SetAudio(a);
  }
}

//---------------------------------------------------------------------------
//------------------------------ Timer: -------------------------------------
//---------------------------------------------------------------------------

void __fastcall TMainForm::Timer1Timer(TObject *Sender)
{
  Timer1->Enabled = 0;
  if(FConnected) //если устройство подключено
  {
    try
    {
      GetState();
      //отрисовка графиков:
      if(FMode == MD_START)
      {
        if(FPointer < LOGN)
        {
          //добавление точки:
          Data1[FPointer] = State.t1;
          Data2[FPointer] = State.t2;
          Data3[FPointer] = State.m1;
          Data4[FPointer] = State.m2;
          Data5[FPointer] = State.r1;
          Data6[FPointer] = State.r2;
          //рисование точки:
          if(!acFreeze->Checked)
            DrawLog(LogPaintBox->Canvas, FPointer, FPointer);
          FPointer++;
          //автоматическое скроллирование:
          if(FAutoScroll && !acFreeze->Checked &&
               FPointer - LogScrollBar->Position >
                 LogPaintBox->Width - GAPL - GAPR)
            LogScrollBar->Position += 100;
        }
        else
        {
          //если конец массивов, стоп:
          acStopExecute(NULL);
        }
      }
    }
    catch(const EInOutError &e) { Disconnect(); }
  }
  else
  {
    //автоматическое подключение:
    if(FAutoConnect && FPortNumber) { Connect(); }
  }
  Timer1->Enabled = 1;
}

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

