// =====================================================
//
//   miniLA_win - RS-232 Decoder
//
//   (c) miniLA Team
//
// =====================================================
//
// This is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or (at your option)
// any later version.
//
// This software is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this package; see the file COPYING.  If not, write to
// the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.

unit dlgDec232;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons,
  uDecUtils, ExtCtrls;

type
  TfrmDec232 = class(TForm)
    cbChannel: TComboBox;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    cbInvert: TCheckBox;
    cbBaudrate: TComboBox;
    cbParity: TComboBox;
    cbStopbit: TComboBox;
    Label5: TLabel;
    cbDataBits: TComboBox;
    btnOK: TBitBtn;
    btnCancel: TBitBtn;
    GroupBox1: TGroupBox;
    gbDecodeRange: TGroupBox;
    rbAllData: TRadioButton;
    rbCursors: TRadioButton;
    procedure btnOKClick(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure btnCancelClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    ChannelSel:  TChannelSel;

    decoded_channel	: integer;
    channel		: cardinal;
    channel_last	: cardinal;

    function D_i(val:cardinal):cardinal;
    function D_o(val:cardinal):cardinal;
    function loc_data_get_value(time:double):boolean;
    function loc_data_find_change:boolean;
  public
    { Public declarations }
  end;

var
  frmDec232: TfrmDec232;

implementation

uses
   uUtils,
   uLATypes,
   uDecData,
   uINIFile,
   dlgDecDisplay,
   dlgMain;

{$R *.dfm}

procedure TfrmDec232.FormCreate(Sender: TObject);
begin
   cbChannel.ItemIndex  := INIFile.ReadInteger('DEC_232','CHANNEL', 0);
   cbInvert.Checked     := INIFile.ReadBool('DEC_232','INVERTED', false);
   cbBaudrate.Text      := INIFile.ReadString('DEC_232','BAUDRATE', '9600');
   cbParity.ItemIndex   := INIFile.ReadInteger('DEC_232','PARITY', 0);
   cbStopBit.ItemIndex  := INIFile.ReadInteger('DEC_232','STOPBITS', 0);
   cbDataBits.ItemIndex := INIFile.ReadInteger('DEC_232','DATABITS', 3);
   rbAllData.Checked    := INIFile.ReadBool('DEC_232','DEC_FULL_RANGE', true);
   rbCursors.Checked	:= not(rbAllData.Checked);
end;


procedure TfrmDec232.FormDestroy(Sender: TObject);
begin
   INIFile.WriteInteger('DEC_232','CHANNEL', cbChannel.ItemIndex);
   INIFile.WriteBool('DEC_232','INVERTED', cbInvert.Checked);
   INIFile.WriteString('DEC_232','BAUDRATE', cbBaudrate.Text);
   INIFile.WriteInteger('DEC_232','PARITY', cbParity.ItemIndex);
   INIFile.WriteInteger('DEC_232','STOPBITS', cbStopBit.ItemIndex);
   INIFile.WriteInteger('DEC_232','DATABITS', cbDataBits.ItemIndex);
   INIFile.WriteBool('DEC_232','DEC_FULL_RANGE', rbAllData.Checked);
end;


procedure TfrmDec232.FormActivate(Sender: TObject);
begin
   ChannelSel := TChannelSel.Create;
   ChannelSel.Add(cbChannel);
end;


function TfrmDec232.D_i(val:cardinal):cardinal;
begin
   result := val shl decoded_channel;
end;


function TfrmDec232.D_o(val:cardinal):cardinal;
begin
   result := (val shr decoded_channel) and $1;
end;


function TfrmDec232.loc_data_get_value(time:double):boolean;
var
   read_err 	: boolean;
begin
   channel_last := channel;
   read_err := data_get_value(time);
   channel := D_o(sample);
   result := read_err;
end;
 

function TfrmDec232.loc_data_find_change:boolean;
var
   read_err	: boolean;
begin   
   channel_last := channel;
   read_err := data_find_change(D_i(1));
   channel := D_o(sample);
   result := read_err;
end;


procedure TfrmDec232.btnOKClick(Sender: TObject);
var
   read_err	: boolean;
   state	: (ST_IDLE, ST_START, ST_DATA, ST_PAR, ST_STOP);
   bit_cnt	: byte;
   rec_byte	: word;
   rec_byte_par	: byte;

   bitrate	: double;
   data_bits	: byte;
   stop_bits	: byte;
   err_msg	: string;
   rep_str	: string;
begin
   err_msg := '';
   bitrate := 0;
   data_bits := 0;
   stop_bits := 0;
   
   frmDecDisplay.lbResults.Clear;

   try
      bitrate := 1/StrToInt(cbBaudrate.Text);
   except
      on EConvertError do
         err_msg := 'baudrate';
   end;
   if bitrate<=0 then
      err_msg := 'baudrate';

   if cbParity.ItemIndex < 0 then
      err_msg := 'parity setting';

   case cbDataBits.ItemIndex of
      0: data_bits := 5;
      1: data_bits := 6;
      2: data_bits := 7;
      3: data_bits := 8;
   else
      err_msg := 'number of data bits';
   end;

   case cbStopBit.ItemIndex of
      0: stop_bits := 1;
      1: stop_bits := 2;
   else
      err_msg := 'number of stop bits';
   end;

   decoded_channel := ChannelSel.GetChannelNumber(cbChannel);
   if decoded_channel < 0 then
      err_msg := 'channel to decode';

   if err_msg <> '' then
    begin
      MessageDlg('Invalid '+err_msg, mtError,[mbOK],0);
      exit;
    end;

   ChannelSel.Free;
   Hide;
   frmMain.StatusBar1.Panels[5].Text := 'Decoding RS-232 protocol ...';
   frmMain.StatusBar1.Repaint;

   if cbInvert.Checked then
      sample_inv := D_i(1)
   else
      sample_inv := 0;

   bit_cnt := 0;
   rec_byte := 0;
   rec_byte_par := 0;
   read_err := false;
   state := ST_IDLE;

   if rbAllData.Checked then
      data_init(0, frmMain.LogPanel.Display.DataHold.MaxPosition)	// initialize sample read routine
   else
      data_init(frmMain.LogPanel.Cursors[0].Position,
		frmMain.LogPanel.Cursors[1].Position);

   frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) start of decoding', [sample_n, TmToStr(time_p,2)]));

//   read_err := data_get_sample;					// get initial value
   channel := D_o(sample);

   while (not read_err) do
      case (state) of
	 ST_IDLE:
	  begin
	    read_err := read_err or loc_data_find_change;	// find beginning of start bit (falling edge)
	    if (read_err) then
	       break;
	    if ((channel_last<>0) and (channel=0)) then
	       state := ST_START;
	  end;

	 // start bit
	 ST_START:
	  begin
	    read_err := read_err or loc_data_get_value(time_p + bitrate/2);	// half of start bit
	    if (read_err) then
	       break;
	    if (channel<>0) then
	       frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) startbit error', [sample_n, TmToStr(time_p,2)]));
	    bit_cnt := 0;
	    rec_byte := 0;
	    rec_byte_par := 0;
	    state := ST_DATA;
	  end;

	 // data bits
	 ST_DATA:
	  begin
	    read_err := read_err or loc_data_get_value(time_p + bitrate);
	    if (read_err) then
	       break;
	    rec_byte := rec_byte or (channel shl bit_cnt);
	    inc(bit_cnt);
	    rec_byte_par := rec_byte_par xor channel;
	    if (bit_cnt = data_bits) then
	     begin
	       bit_cnt := 0;
	       if (cbParity.ItemIndex = 0) then
	          state := ST_STOP
	       else
		  state := ST_PAR;
	     end;
	   end;

	 // parity bit
	 ST_PAR:
	  begin
	    read_err := read_err or loc_data_get_value(time_p + bitrate);
	    if (read_err) then
	       break;
	    rec_byte_par := rec_byte_par xor channel;
	    case (cbParity.ItemIndex) of
	       0: // NONE
		  rec_byte_par := 0;
	       1: // ODD
		  rec_byte_par := rec_byte_par xor 1;
	       2: // EVEN:
		  rec_byte_par := rec_byte_par xor 0;
	       3: // MARK:
		  rec_byte_par := channel xor 1;
	       4: // SPACE:
		  rec_byte_par := channel xor 0;
	    end;
	    if (rec_byte_par<>0) then
	       frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) parity error', [sample_n, TmToStr(time_p,2)]));
	    state := ST_STOP;
	  end;

	 // stop bit(s)
	 ST_STOP:
	  begin
	    read_err := read_err or loc_data_get_value(time_p + bitrate);
	    if (read_err) then
	       break;
	    inc(bit_cnt);
	    if (channel=0) then
	       frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) stop bit %d error', [sample_n, TmToStr(time_p,2),bit_cnt]));
	    if (bit_cnt = stop_bits) then
	     begin
	       if (rec_byte >= 31) then
		  rep_str :=chr(rec_byte)
	       else
		  rep_str := '';
	       frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) 0x%.2X %s', [sample_n, TmToStr(time_p,2),rec_byte,rep_str]));
	       state := ST_IDLE;
	     end;
	  end;

      end; // end case

   if (state <> ST_IDLE) then
      frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) last byte not fully decoded.', [sample_n, TmToStr(time_p,2)]));

   frmDecDisplay.lbResults.Items.Add(Format('%d (%ss) end of decoding', [sample_n, TmToStr(time_p,2)]));   ModalResult := mrOK;

   frmDecDisplay.Caption := 'Decoded data - RS-232';
   frmDecDisplay.Show;
   frmMain.StatusBar1.Panels[5].Text := '';
   frmMain.StatusBar1.Repaint;
end;


procedure TfrmDec232.btnCancelClick(Sender: TObject);
begin
   ChannelSel.Free;
end;



end.

