unit Main;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, CommReader, ExtCtrls, ComCtrls, IniFiles, Menus, ImgList,
  ToolWin, ActnList, Grids, JITStringGrid, Math, StrUtils;

type
  TfrmMain = class(TForm)
    pnlConfig: TPanel;
    mmo: TRichEdit;
    lblPort: TLabel;
    cbMain: TCoolBar;
    ToolBar1: TToolBar;
    btnStart: TToolButton;
    btnStop: TToolButton;
    il: TImageList;
    ilHot: TImageList;
    ilDisabled: TImageList;
    mnuMain: TMainMenu;
    miFile: TMenuItem;
    miAction: TMenuItem;
    miStartEmu: TMenuItem;
    miStopEmu: TMenuItem;
    miView: TMenuItem;
    miViewConfig: TMenuItem;
    miViewTest: TMenuItem;
    aclMain: TActionList;
    acStartEmu: TAction;
    acStopEmu: TAction;
    acViewConfiguration: TAction;
    ToolButton1: TToolButton;
    btnViewConfig: TToolButton;
    pnlTest: TPanel;
    btnOpen: TButton;
    btnClose: TButton;
    btnSetRTS: TButton;
    btnSetDTR: TButton;
    btnClrRTS: TButton;
    btnClrDTR: TButton;
    btnWrite: TButton;
    btnRead: TButton;
    Button1: TButton;
    pnlStatus: TPanel;
    Label3: TLabel;
    shpTester: TShape;
    Label2: TLabel;
    edtBlock: TEdit;
    acViewTest: TAction;
    ToolButton2: TToolButton;
    cbPort: TComboBox;
    acSaveLog: TAction;
    acExitProgram: TAction;
    btnSaveLog: TToolButton;
    ToolButton3: TToolButton;
    ToolButton4: TToolButton;
    btnExit: TToolButton;
    miSaveLog: TMenuItem;
    N1: TMenuItem;
    miExit: TMenuItem;
    lblConnectionType: TLabel;
    cbConnection: TComboBox;
    lblInterface: TLabel;
    cbInterface: TComboBox;
    lblProto: TLabel;
    cbProto: TComboBox;
    Button2: TButton;
    Button3: TButton;
    pb: TPaintBox;
    sb: TScrollBar;
    pmSG: TPopupMenu;
    Clear1: TMenuItem;
    splsg: TSplitter;
    PageControl1: TPageControl;
    tsLog: TTabSheet;
    sg: TJITStringGrid;
    tsMode1: TTabSheet;
    pcMode1: TPageControl;
    tsSupportedCPTPIDs: TTabSheet;
    tsMonitorStatus: TTabSheet;
    ScrollBox1: TScrollBox;
    Label4: TLabel;
    edtDTCs: TEdit;
    chkMIL: TCheckBox;
    Label1: TLabel;
    lvCTS: TListView;
    lvCTR: TListView;
    lvNCTS: TListView;
    lvNCTR: TListView;
    lvCPTPIDs: TListView;
    pnlPIDHelp: TPanel;
    lblPIDHelp: TLabel;
    Panel1: TPanel;
    cbPriority: TComboBox;
    Label5: TLabel;
    pnlLogConfig: TPanel;
    lblMaxHexLines: TLabel;
    edtMaxHexLines: TEdit;
    procedure FormCreate(Sender: TObject);
    procedure btnOpenClick(Sender: TObject);
    procedure btnSetRTSClick(Sender: TObject);
    procedure btnClrRTSClick(Sender: TObject);
    procedure btnWriteClick(Sender: TObject);
    procedure btnSetDTRClick(Sender: TObject);
    procedure btnClrDTRClick(Sender: TObject);
    procedure btnReadClick(Sender: TObject);
    procedure btnCloseClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure emuThreadFeedback(var msg: TMessage); message $2000;
    procedure Button1Click(Sender: TObject);
    procedure acStartEmuExecute(Sender: TObject);
    procedure acStopEmuExecute(Sender: TObject);
    procedure btnViewConfigClick(Sender: TObject);
    procedure acViewConfigurationExecute(Sender: TObject);
    procedure acViewTestExecute(Sender: TObject);
    procedure EnableStart(f: Boolean);
    procedure acSaveLogExecute(Sender: TObject);
    procedure acExitProgramExecute(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure SGGetText(Sender: TObject; ACol, ARow: Longint;
      var Value: string);
    procedure SGGetTextColor(Sender: TObject; ACol, ARow: Longint;
      var Color: TColor);
    procedure pbPaint(Sender: TObject);
    procedure sbChange(Sender: TObject);
    procedure Clear1Click(Sender: TObject);
    procedure lvCPTPIDsDblClick(Sender: TObject);
    procedure cbPriorityChange(Sender: TObject);
    procedure edtMaxHexLinesChange(Sender: TObject);
    procedure edtMaxHexLinesKeyPress(Sender: TObject; var Key: Char);
  private
    comm: THandle;
    cr: TReaderThread;
    lstRows: TList;
    lstBounces: TList;
    giMin: Integer;
    iMaxHexLines: Integer;
  public
    procedure Log(sz: string);
  end;

var
  frmMain: TfrmMain;

implementation

uses OBDBytes;

{$R *.DFM}

procedure TfrmMain.SGGetTextColor(Sender: TObject; ACol, ARow: Longint;
  var Color: TColor);
var
  i: Integer;
begin
  if ARow = 0 then
  begin
    Color := clBlack;
  end else
  if ARow > lstRows.Count then
  begin
    Color := clBlack;
  end else
  if ACol in [0, 1] then
  begin
    Color := clBlack;
  end else
  if ACol - 1 >= TList(lstRows[ARow - 1]).Count then
  begin
    Color := clBlack;
  end else
  begin
    i := Integer(TList(lstRows[ARow - 1])[ACol - 1]) and $FFFF0000;
    if i = ecuSendChar then
    begin
      Color := clBlue;
    end else
    if i = ecuReadChar then
    begin
      Color := clGreen;
    end;
  end;
end;

procedure TfrmMain.SGGetText(Sender: TObject; ACol, ARow: Longint;
  var Value: string);
begin
  if ARow = 0 then
  begin
    if ACol = 0 then
    begin
      Value := 'Line';
    end else
    if ACol = 1 then
    begin
      Value := 'Comment';
    end else
    begin
      Value := IntToStr(ACol);
    end;
  end else
  if ARow > lstRows.Count then
  begin
    Value := ''
  end else
  if ACol = 0 then
  begin
    Value := IntToStr(ARow);
  end else
  if ACol = 1 then
  begin
    if Integer(TList(lstRows[ARow - 1])[0]) = 0 then
    begin
      Value := '';
    end else
    begin
      Value := TString(TList(lstRows[ARow - 1])[0]).sz;
    end;
  end else
  if ACol - 1 >= TList(lstRows[ARow - 1]).Count then
  begin
    Value := '';
  end else
  begin
    Value := Format('%.2x', [Cardinal(TList(lstRows[ARow - 1])[ACol - 1]) and $FF]);
  end;
end;

procedure TfrmMain.EnableStart(f: Boolean);
begin
  if f then
  begin
    acStartEmu.Enabled := False;
    acStopEmu.Enabled := True;

    cbPort.Enabled := False;
    lblPort.Enabled := False;

    cbConnection.Enabled := False;
    lblConnectionType.Enabled := False;

    cbInterface.Enabled := False;
    lblInterface.Enabled := False;

    cbProto.Enabled := False;
    lblProto.Enabled := False;
  end else
  begin
    acStartEmu.Enabled := True;
    acStopEmu.Enabled := False;

    cbPort.Enabled := True;
    lblPort.Enabled := True;

    cbConnection.Enabled := True;
    lblConnectionType.Enabled := True;

    cbInterface.Enabled := True;
    lblInterface.Enabled := True;

    cbProto.Enabled := True;
    lblProto.Enabled := True;
  end;
end;

procedure TfrmMain.emuThreadFeedback(var msg: TMessage);
var
  lstRow: TList;
  w: Integer;
begin
  case TEMUFeedback(msg.WParam) of
    emuTesterPresent:
    begin
      if shpTester.Brush.Color = clRed then
      begin
        shpTester.Brush.Color := clLime;
      end else
      begin
        shpTester.Brush.Color := clRed;
      end;
    end;
    emuStart:
    begin
      Log('Emulator started');
      EnableStart(True);
    end;
    emuStop:
    begin
      Log('Emulator stopped');
      Log('');
      shpTester.Brush.Color := clRed;
      EnableStart(False);
    end;
    emuChar:
    begin
      if lstRows.Count = 0 then
      begin
        lstRow := TList.Create;
        lstRows.Add(lstRow);
        lstRow.Add(nil);
        sg.RowCount := 2;
      end else
      begin
        lstRow := lstRows[lstRows.Count - 1];
      end;
      lstRow.Add(Pointer(msg.LParam));
      if sg.ColCount < lstRow.Count + 1 then
      begin
        sg.ColCount := lstRow.Count + 1;
      end;
      sg.Invalidate;
    end;
    emuRow:
    begin
      lstRow := TList.Create;

      while lstRows.Count >= iMaxHexLines do
      begin
        if TList(lstRows[0]).Count > 0 then
        begin
          if Integer(TList(lstRows[0])[0]) <> 0 then
          begin
            TString(TList(lstRows[0])[0]).Free;
          end;
        end;
        TList(lstRows[0]).Free;
        lstRows.Delete(0);
      end;

      lstRows.Add(lstRow);
      sg.RowCount := lstRows.Count + 1;
      sg.Selection :=
        TGridRect(
          Rect(sg.Selection.Left, sg.RowCount - 1, sg.Selection.Left, sg.RowCount - 1));
      if msg.LParam <> 0 then
      begin
        lstRow.Add(Pointer(msg.LParam));
      end else
      begin
        lstRow.Add(0);
      end;
      if sg.RowCount - sg.VisibleRowCount > 0 then
      begin
        sg.TopRow := sg.RowCount - sg.VisibleRowCount;
      end;
    end;
    emuBounce:
    begin
      lstBounces.Add(Pointer(msg.LParam));

      w := (pb.Width - 10) div 30;
      sb.Max := Max(lstBounces.Count - w, 0);
      giMin := Max(lstBounces.Count - w, 0);
      sb.Position := giMin;

      pb.Invalidate;
    end;
  end;
end;

procedure TfrmMain.Log(sz: string);
begin
  mmo.Lines.Add(sz);
end;

// iCols - number of cols in the array
// iFillCols - number of cols to fill in the list view. the other cols are
// attached to the Data of the list item as a list of TString object
procedure FillListView(lv: TListView; a: array of String; iCols: Integer = 1; iFillCols: Integer = 1);
var
  i: Integer;
  j: Integer;
  li: TListItem;
  lst: TList;
  so: TString;
begin
  for i := 0 to (Length(a) div iCols) - 1 do
  begin
    li := lv.Items.Add;
    li.Caption := a[i * iCols];
    for j := 1 to iFillCols - 1 do
    begin
      li.SubItems.Add(a[i * iCols + j]);
    end;
    if iCols > iFillCols then
    begin
      lst := TList.Create;
      li.Data := lst;
    end;
    for j := iFillCols to iCols - 1 do
    begin
      lst.Add(TString.Create(a[i * iCols + j]));
    end;
  end;
end;

function ColWidthsToString(lv: TListView): string;
var
  i: Integer;
begin
  for i := 0 to lv.Columns.Count - 1 do
  begin
    Result := Result + ', ' + IntToStr(lv.Columns[i].Width);
  end;
  Delete(Result, 1, 2);
end;

procedure StringToColWidths(lv: TListView; sz: string);
var
  i: Integer;
  sl: TStringList;
begin
  sl := TStringList.Create;
  try
    ExtractStrings([','], [' '], PChar(sz), sl);
    for i := 0 to lv.Columns.Count - 1 do
    begin
      if i > sl.Count - 1 then
        Break;
      lv.Columns[i].Width := StrToInt(sl[i]);
    end;
  finally
  end;
end;

function StringToValue(sz: string): string;
var
  i: Integer;
begin
  Result := '';
  for i := 1 to Length(sz) do
  begin
    Result := Result + Format(' 0x%.2x', [Byte(sz[i])]);
  end;
  Delete(Result, 1, 1);
end;

procedure TfrmMain.FormCreate(Sender: TObject);
var
  i: Integer;
  j: Integer;
  sz: string;
  Result: Integer;
  ini: TIniFile;
  iLeft: Integer;
  lst: TList;
  iDataLength: Integer;
  szValue: string;
  sl: TStringList;
  b: Byte;
begin
  FillListView(lvCTS, ContinuousTests);
  FillListView(lvCTR, ContinuousTests);
  FillListView(lvNCTS, NonContinuousTests);
  FillListView(lvNCTR, NonContinuousTests);
  FillListView(lvCPTPIDs, PIDs, 3, 2);

  cbPriority.ItemIndex := 2;

  sg.ColWidths[0] := 30;
  sg.ColWidths[1] := 50;
  sg.OnGetTextColor := SGGetTextColor;
  lstRows := TList.Create;

  lstBounces := TList.Create;
  sb.Max := 0;
  giMin := 0;

  cbConnection.Items.AddObject('Nullmodem', TObject(conNullmodem));
  cbConnection.Items.AddObject('Jeff', TObject(conJeff));
  cbInterface.Items.AddObject('Jeff', TObject(intJeff));
  cbInterface.Items.AddObject('AutoEnginuity', TObject(intAE));
  cbInterface.Items.AddObject('ELM323', TObject(intELM323));
  cbProto.Items.AddObject('KW1281', TObject(protoKW1281));
  cbProto.Items.AddObject('KW2000', TObject(protoKW2000));

  cbPort.Items.Clear;
  for i := 1 to 16 do
  begin
    comm := CreateFile(PChar('COM' + IntToStr(i)), 0, 0,
      nil, OPEN_EXISTING, 0, 0);
    try
      if (comm = INVALID_HANDLE_VALUE) and (GetLastError <> 5) then
      begin
      end else
      begin
        cbPort.Items.Add('COM' + IntToStr(i));
      end;
    finally
      CloseHandle(comm);
    end;
  end;

  ini := TIniFile.Create('ECUEMU.ini');
  sl := TStringList.Create;
  try
    cbPriority.ItemIndex := ini.ReadInteger('GENERAL', 'Priority', cbPriority.ItemIndex);
    cbPort.ItemIndex := cbPort.Items.IndexOf(ini.ReadString('GENERAL', 'COM', cbPort.Text));
    if cbPort.ItemIndex = -1 then
      cbPort.ItemIndex := 0;

    cbConnection.ItemIndex := cbConnection.Items.IndexOf(ini.ReadString('GENERAL', 'Connection', cbConnection.Text));
    if cbConnection.ItemIndex = -1 then
      cbConnection.ItemIndex := 0;

    cbInterface.ItemIndex := cbInterface.Items.IndexOf(ini.ReadString('GENERAL', 'Interface', cbInterface.Text));
    if cbInterface.ItemIndex = -1 then
      cbInterface.ItemIndex := 0;

    cbProto.ItemIndex := cbProto.Items.IndexOf(ini.ReadString('GENERAL', 'Protocol', cbProto.Text));
    if cbProto.ItemIndex = -1 then
      cbProto.ItemIndex := 0;

    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', Integer(WindowState)));
    if not ini.ReadBool('GENERAL', 'ConfigVisible', False) then
    begin
      acViewConfiguration.Execute;
    end;
    if not ini.ReadBool('GENERAL', 'TestVisible', False) then
    begin
      acViewTest.Execute;
    end;

    iMaxHexLines := 100;
    iMaxHexLines := ini.ReadInteger('GENERAL', 'MaxHexLines', iMaxHexLines);
    edtMaxHexLines.Text := IntToStr(iMaxHexLines);

    sz := ColWidthsToString(lvCPTPIDs);
    sz := ini.ReadString('MODE01PIDs', 'ColWidths', sz);
    StringToColWidths(lvCPTPIDs, sz);
    for i := 0 to lvCPTPIDs.Items.Count - 1 do
    begin
      lst := lvCPTPIDs.Items[i].Data;
      iDataLength := StrToInt(TString(lst[0]).sz);
      SetLength(sz, iDataLength);
      FillChar(sz[1], iDataLength, 0);

      szValue := ini.ReadString('MODE01PIDs', lvCPTPIDs.Items[i].Caption, '');
      if szValue = '' then
      else
      begin
        sl.Clear;
        ExtractStrings([' '], [], PChar(szValue), sl);
        if sl.Count > 0 then
        begin
          if sl[0] = '01' then
          begin
            lvCPTPIDs.Items[i].Checked := True;
          end;
          sz := '';
          for j := 1 to sl.Count - 1 do
          begin
            b := Hex2Dec(sl[j]);
            sz := sz + Char(b);
          end;
        end;
      end;

      lst.Add(TString.Create(sz));
      szValue := StringToValue(sz);
      lvCPTPIDs.Items[i].SubItems.Add(szValue);
    end;

  finally
    sl.Free;
    ini.Free;
  end;

  EnableStart(False);
end;

procedure TfrmMain.btnOpenClick(Sender: TObject);
var
  cc: _COMMCONFIG;
  ccsize: Cardinal;
  ct: _COMMTIMEOUTS;
  szComm: string;
begin
  szComm := cbPort.Text;
  comm := CreateFile(PChar(szComm), GENERIC_READ or GENERIC_WRITE, 0,
    nil, OPEN_EXISTING, 0, 0);
  if comm = INVALID_HANDLE_VALUE then
  begin
    Log('Error opening communication device');
    Exit;
  end;

  ccsize := SizeOf(cc);
  if not GetCommConfig(comm, cc, ccsize) then
  begin
    Log('GetCommConfig: ' + SysErrorMessage(GetLastError));
    btnClose.Click;
    Exit;
  end;

  cc.dcb.BaudRate := 10400;
  cc.dcb.ByteSize := 8;
  cc.dcb.StopBits := 0;
  cc.dcb.Flags := 1; // fBinary

  {if not CommConfigDialog(PChar(szComm), Handle, cc) then
  begin
    Log('CommConfigDialog: ' + SysErrorMessage(GetLastError));
    btnClose.Click;
    Exit;
  end;}

  cc.dcb.BaudRate := 10400;

  if not SetCommConfig(comm, cc, ccsize) then
  begin
    Log('SetCommConfig: ' + SysErrorMessage(GetLastError));
    btnClose.Click;
    Exit;
  end;

  GetCommTimeouts(comm, ct);
  ct.ReadIntervalTimeout := 0;
  ct.ReadTotalTimeoutConstant := 1000;
  ct.ReadTotalTimeoutMultiplier := 200;
  ct.WriteTotalTimeoutConstant := 0;
  ct.WriteTotalTimeoutMultiplier := 0;
  SetCommTimeouts(comm, ct);
end;

procedure TfrmMain.btnSetRTSClick(Sender: TObject);
begin
  if not EscapeCommFunction(comm, SETRTS) then
  begin
    Log('SETRTS: ' + SysErrorMessage(GetLastError));
    Exit;
  end;
end;

procedure TfrmMain.btnClrRTSClick(Sender: TObject);
begin
  if not EscapeCommFunction(comm, CLRRTS) then
  begin
    Log('CLRRTS: ' + SysErrorMessage(GetLastError));
    Exit;
  end;
end;

procedure TfrmMain.btnWriteClick(Sender: TObject);
var
  bw: Cardinal;
  br: Cardinal;
  sz: string;
  i: Integer;
begin
  for i := 0 to 1023 do
    sz := sz + 'a';
  if not WriteFile(comm, sz[1], Length(sz), bw, nil) then
  begin
    Log('WriteFile: ' + SysErrorMessage(GetLastError));
    Exit;
  end;
  if not ReadFile(comm, sz[1], Length(sz), br, nil) then
  begin
    Log('ReadFile: ' + SysErrorMessage(GetLastError));
    Exit;
  end;
  SetLength(sz, br);
  Log(sz);
end;

procedure TfrmMain.btnSetDTRClick(Sender: TObject);
begin
  if not EscapeCommFunction(comm, SETDTR) then
  begin
    Log('SETDTR: ' + SysErrorMessage(GetLastError));
    Exit;
  end;
end;

procedure TfrmMain.btnClrDTRClick(Sender: TObject);
begin
  if not EscapeCommFunction(comm, CLRDTR) then
  begin
    Log('CLRDTR: ' + SysErrorMessage(GetLastError));
    Exit;
  end;
end;

procedure TfrmMain.btnReadClick(Sender: TObject);
var
  sz: string;
  sz1: string;
  br: Cardinal;
  i: Integer;
begin
  sz := 'asdasd';
  if not ReadFile(comm, sz[1], Length(sz), br, nil) then
  begin
    Log('ReadFile: ' + SysErrorMessage(GetLastError));
    Exit;
  end;
  SetLength(sz, br);
  sz1 := '';
  for i := 1 to Length(sz) do
  begin
    sz1 := sz1 + Format('%.2x ', [Byte(sz[i])]);
  end;
  Log(sz1);
end;

procedure TfrmMain.btnCloseClick(Sender: TObject);
begin
  CloseHandle(comm);
end;

procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
var
  ini: TIniFile;
  i: Integer;
  j: Integer;
  lst: TList;
  sz: string;
  szValue: string;
begin
  ini := TIniFile.Create('ECUEMU.ini');
  try
    ini.WriteString('GENERAL', 'COM', cbPort.Text);
    ini.WriteString('GENERAL', 'Connection', cbConnection.Text);
    ini.WriteString('GENERAL', 'Interface', cbInterface.Text);
    ini.WriteString('GENERAL', 'Protocol', cbProto.Text);
    ini.WriteInteger('GENERAL', 'WindowState', Integer(WindowState));
    ini.WriteInteger('GENERAL', 'MaxHexLines', iMaxHexLines);
    ini.WriteInteger('GENERAL', 'Priority', cbPriority.ItemIndex);
    if WindowState <> wsNormal then
    begin
      WindowState := wsNormal;
    end;
    ini.WriteInteger('GENERAL', 'Left', Left);
    ini.WriteInteger('GENERAL', 'Top', Top);
    ini.WriteInteger('GENERAL', 'Width', Width);
    ini.WriteInteger('GENERAL', 'Height', Height);
    ini.WriteBool('GENERAL', 'ConfigVisible', pnlConfig.Visible);
    ini.WriteBool('GENERAL', 'TestVisible', pnlTest.Visible);

    sz := ColWidthsToString(lvCPTPIDs);
    ini.WriteString('MODE01PIDs', 'ColWidths', sz);
    for i := 0 to lvCPTPIDs.Items.Count - 1 do
    begin
      if lvCPTPIDs.Items[i].Checked then
        sz := '01'
      else
        sz := '00';
      lst := lvCPTPIDs.Items[i].Data;
      szValue := TString(lst[1]).sz;
      for j := 1 to Length(szValue) do
      begin
        sz := sz + Format(' %.2x', [Byte(szValue[j])]);
      end;
      ini.WriteString('MODE01PIDs', lvCPTPIDs.Items[i].Caption, sz);
    end;
  finally
    ini.Free;
  end;
end;

procedure TfrmMain.Button1Click(Sender: TObject);
var
  ms: Cardinal;
begin
  GetCommModemStatus(comm, ms);
  mmo.Lines.Add(IntToStr(ms));
end;

procedure TfrmMain.acStartEmuExecute(Sender: TObject);
begin
  cr := TReaderThread.Create(True);
  cr.szComm := cbPort.Text;

  cr.Connection := TConnectionType(cbConnection.Items.Objects[cbConnection.ItemIndex]);
  cr.IntFace := TInterfaceType(cbInterface.Items.Objects[cbInterface.ItemIndex]);
  cr.Proto := TProtocolType(cbProto.Items.Objects[cbProto.ItemIndex]);

  with cr do
  begin
    hcomm := CreateFile(PChar(szComm), GENERIC_READ or GENERIC_WRITE, 0,
      nil, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
    if hcomm = INVALID_HANDLE_VALUE then
    begin
      Log('CreateFile: ' + SysErrorMessage(GetLastError));
      Exit;
    end;
  end;
  cr.Resume;
end;

procedure TfrmMain.acStopEmuExecute(Sender: TObject);
begin
  CloseHandle(cr.hcomm);
end;

procedure TfrmMain.btnViewConfigClick(Sender: TObject);
begin
  {}
end;

procedure TfrmMain.acViewConfigurationExecute(Sender: TObject);
begin
  if pnlConfig.Visible then
  begin
    pnlConfig.Visible := False;
  end else
  begin
    pnlConfig.Visible := True;
  end;
end;

procedure TfrmMain.acViewTestExecute(Sender: TObject);
begin
  if pnlTest.Visible then
  begin
    pnlTest.Visible := False;
  end else
  begin
    pnlTest.Visible := True;
  end;
end;

procedure TfrmMain.acSaveLogExecute(Sender: TObject);
var
  sd: TSaveDialog;
begin
  sd := TSaveDialog.Create(Self);
  try
    sd.Filter := 'Log files (*.log)|*.log|All files (*.*)|*.*';
    if sd.Execute then
    begin
      if (ExtractFileExt(sd.FileName) = '') and
        (sd.FilterIndex = 1) then
        sd.FileName := sd.FileName + '.log';
      mmo.Lines.SaveToFile(sd.FileName);
    end;
  finally
    sd.Free;
  end;
end;

procedure TfrmMain.acExitProgramExecute(Sender: TObject);
begin
  Close;
end;

procedure TfrmMain.Button2Click(Sender: TObject);
begin
  SetCommBreak(comm);
end;

procedure TfrmMain.Button3Click(Sender: TObject);
begin
  ClearCommBreak(comm);
end;

procedure TfrmMain.pbPaint(Sender: TObject);
var
  c: TCanvas;
  i: Integer;
  w: Integer;
  iMin: Integer;
  iMax: Integer;
  iPos: Integer;
begin
  c := pb.Canvas;
  c.Brush.Color := clBlack;
  c.Pen.Color := clBlack;
  c.Rectangle(0, 0, pb.Width, pb.Height);
  c.Pen.Color := clLime;

  c.MoveTo(10, 50);
  c.Font.Name := 'Arial';
  c.Font.Size := 8;
  c.Font.Color := clLime;

  iMin := giMin;
  iMax := Min(lstBounces.Count, iMin + w);

  for i := iMin to iMax - 1 do
  begin
    iPos := i - iMin;
    if Integer(lstBounces[i]) and $FF = 0 then
    begin
      c.LineTo(10 + iPos * 30, 50);
      c.LineTo(10 + (iPos + 1) * 30, 50);
    end else
    begin
      c.LineTo(10 + iPos * 30, 10);
      c.LineTo(10 + (iPos + 1) * 30, 10);
    end;
  end;
  for i := iMin to iMax - 1 do
  begin
    iPos := i - iMin;
    case Integer(lstBounces[i]) and not $FF of
      ecuBounceStop:
      begin
        c.TextOut(10 + iPos * 30, 65, 'Stop');
      end;
      ecuBounceStart:
      begin
        c.TextOut(10 + iPos * 30, 65, 'Start');
      end;
      ecuBounceParity:
      begin
        c.TextOut(10 + iPos * 30, 65, 'Parity');
      end;
      ecuBounceData:
      begin
        if Integer(lstBounces[i]) and $FF <> 0 then
          c.TextOut(10 + iPos * 30 + 10, 65, '0')
        else
          c.TextOut(10 + iPos * 30 + 10, 65, '1');
      end;
    end;
  end;
end;

procedure TfrmMain.sbChange(Sender: TObject);
begin
  giMin := sb.Position;
  pb.Invalidate;
end;

procedure TfrmMain.Clear1Click(Sender: TObject);
var
  i: Integer;
begin
  sg.RowCount := 2;
  sg.ColCount := 1;
  for i := 0 to lstRows.Count - 1 do
  begin
    if TList(lstRows[i]).Count > 0 then
    begin
      if Integer(TList(lstRows[i])[0]) <> 0 then
      begin
        TString(TList(lstRows[i])[0]).Free;
      end;
    end;
    TList(lstRows[i]).Free;
  end;
  lstRows.Clear;
end;

procedure TfrmMain.lvCPTPIDsDblClick(Sender: TObject);
var
  li: TListItem;
  lst: TList;
  sz: string;
begin
  li := lvCPTPIDs.Selected;
  if li = nil then
    Exit;
  lst := li.Data;
  sz := TString(lst[1]).sz;
  if frmOBDBytes.Execute(sz) then
  begin
    TString(lst[1]).sz := sz;
    li.SubItems[1] := StringToValue(sz);
  end;
end;

procedure TfrmMain.cbPriorityChange(Sender: TObject);
begin
  case cbPriority.ItemIndex of
    0: SetPriorityClass(GetCurrentProcess, REALTIME_PRIORITY_CLASS);
    1: SetPriorityClass(GetCurrentProcess, HIGH_PRIORITY_CLASS);
    2: SetPriorityClass(GetCurrentProcess, NORMAL_PRIORITY_CLASS);
    3: SetPriorityClass(GetCurrentProcess, IDLE_PRIORITY_CLASS);
  end;
end;

procedure TfrmMain.edtMaxHexLinesChange(Sender: TObject);
begin
  if IntToStr(iMaxHexLines) <> Trim(edtMaxHexLines.Text) then
  begin
    edtMaxHexLines.Color := $DDFFFF;
  end else
  begin
    edtMaxHexLines.Color := clWindow;
  end;
end;

procedure TfrmMain.edtMaxHexLinesKeyPress(Sender: TObject; var Key: Char);
begin
  if Key = #13 then
  begin
    try
      iMaxHexLines := StrToInt(edtMaxHexLines.Text);
      edtMaxHexLines.Color := clWindow;
    except
    end;
    edtMaxHexLines.SelectAll;
  end;
end;

end.
