unit iTerminal;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Buttons, ExtCtrls, ScktComp, INIFiles, uss;

const
  OFFLINE = FALSE;
  ONLINE  = TRUE;
  LOGFILENAME = 'Journal.txt';
  SYSFILENAME = 'Terminal.ini';

type
   TTellerInfo = record
      ZONENO, BRANCH, TELLER: String;
      RANK, STATUS: Integer;
      WorkStatus: Boolean;
   end;

   TStartupParam = record
      HostAddr, HostPort: String;
      RequestHead, ProcessHead: String;
      Internal, Normal, Abnormal: String;
      ZONE: String;
   end;

   TTransParams = record
      HostAddr, HostPort: String;
      TrxCode, Teller, Branch, TranSqno, SysDate: String
   end;

  TTeller = class(TForm)
    Panel1: TPanel;
    Label1: TLabel;
    Edit1: TEdit;
    BitBtn1: TBitBtn;
    BitBtn2: TBitBtn;
    GroupBox1: TGroupBox;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Label6: TLabel;
    Label2: TLabel;
    Label9: TLabel;
    Timer1: TTimer;
    procedure BitBtn1Click(Sender: TObject);
    procedure BitBtn2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure FormDestroy(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private declarations }
    CurTeller: TTellerInfo;
    StartupParam: TStartupParam;
    TransParam: TTransParams;
    Journal:TStringList;
    function IsReady:Boolean;
    function Run(LibId, WindowId:String;sinComm:TSinComm):TSoutComm;
//    function RACPKG(rinComm: TRinComm):String;
//    function RACUNPKG(routString: String):TRoutComm;
    function RAC(TRXD: String):TSoutComm;
    function RAN(routComm: TSoutComm):String;
    function RoutAnalysis(routComm: TSoutComm;var Response:String):Boolean;
    function SoutAnalysis(soutComm: TSoutComm;var Dest:String):Boolean;
  public
    { Public declarations }
    sinComm: TSinComm;
    soutComm: TSoutComm;
    tinComm: TTinComm;
    toutComm: TToutComm;
    ShowBuf:String;
    IsCommunicating:Boolean;
    procedure Log(ID,Description:String);
    procedure InitSetting;
    procedure InitTeller(ZoneNo, Brno, Teller: String; Rank, Status: Integer;WorkStatus:Boolean);
  end;

var
  Teller: TTeller;

implementation

{$R *.DFM}
uses emapi;

const
   Title='终端设备';

function TTeller.RAC(TRXD: String):TSoutComm;
var
   iBuffer, oBuffer: String;
   HostAddr, HostPort: String;
   sin: TBuffer;
begin
{********************************************************}
{*** Set tinComm for Transaction of Request and Check ***}
{********************************************************}
   sinComm.SIHOST := StartupParam.HostAddr;
   sinComm.SIPORT := StartupParam.HostPort;
   sinComm.SITRXSRVC := StartupParam.RequestHead;
   sinComm.SITRXD := TRXD;
   sinComm.SITRXSQNO := '0';
   sinComm.SITELLER := CurTeller.TELLER;
   sinComm.SIZONENO := CurTeller.ZONENO;
   sinComm.SIBRANCH := CurTeller.BRANCH;
   sinComm.SISTATUS := IntToStr(CurTeller.STATUS);
   sinComm.SIRANK := IntToStr(CurTeller.RANK);
   sinComm.SISYSDATE := '9999-12-31';
{********************************************************}
{*** Set tinComm for Transaction of Request and Check ***}
{********************************************************}
   iBuffer := packTinComm(sinComm, sin);

   Log(FormatDateTime('yyyy-mm-dd hh:mm:ss',now),iBuffer);
   try
     Netreq_Request(sinComm.SIHOST, sinComm.SIPORT, iBuffer, oBuffer);
   except
     oBuffer:=Ut_IntoPackage(ROERRORCOMMPT,ROSTATERRORCOMMPT);
     Log(FormatDateTime('yyyy-mm-dd hh:mm:ss',now),oBuffer);
     Result := unpackToutComm(oBuffer);
     exit;
   end;
   Log(FormatDateTime('yyyy-mm-dd hh:mm:ss',now),oBuffer);

   Result := unpackToutComm(oBuffer);
end;

function TTeller.RAN(routComm: TSoutComm):String;
var
   iBuffer, oBuffer: String;
   HostAddr, HostPort: String;
   sinComm: TSinComm;
   soutComm: TSoutComm;
begin
   sinComm.SIHOST := StartupParam.HostAddr;
   sinComm.SIPORT := StartupParam.HostPort;

   sinComm.SITRXSRVC := routComm.SOFIELD04; // 服务标识
   sinComm.SITRXD := Edit1.Text;
   sinComm.SITELLER := CurTeller.TELLER;
   sinComm.SIZONENO := CurTeller.ZONENO;
   sinComm.SIBRANCH := CurTeller.BRANCH;
   sinComm.SISTATUS := IntToStr(CurTeller.STATUS);
   sinComm.SIRANK := IntToStr(CurTeller.RANK);
   sinComm.SITRXSQNO := routComm.SOFIELD00; // 交易序号
   sinComm.SISYSDATE := routComm.SOFIELD03; // 系统日期

//   Log(FormatDateTime('yyyy-mm-dd hh:mm:ss',now),iBuffer);

   soutComm := Run(routComm.SOFIELD01, routComm.SOFIELD02, sinComm);

   SoutAnalysis(soutComm, Result);

//   Log(FormatDateTime('yyyy-mm-dd hh:mm:ss',now),oBuffer);
end;

function TTeller.RoutAnalysis(routComm: TSoutComm;var Response:String):Boolean;
var
  Flag: Integer;
begin
  Try
     Flag := StrToInt(routComm.SOOVERID);
  except
     Response := routComm.SOOVERID;
     Result := False;
     Exit;
  end;
  Case Flag of
     RID000 : begin
                Result := True;
                Response := '交易处理成功';
              end;
     RID002 : begin
                Result := False;
                Response := Format('交易未找到。(%s)',[routComm.SOSTATEMENT]);
              end;
     RID006 : begin
                Result := False;
                Response := Format('非网点柜员。(%s)',[routComm.SOSTATEMENT]);
              end;
     RID008 : begin
                Result := False;
                Response := Format('权限不足。(%s)',[routComm.SOSTATEMENT]);
              end;
     RID007 : begin
                Result := False;
                Response := Format('状态非法。(%s)',[routComm.SOSTATEMENT]);
              end;
     RID009 : begin
                Result := False;
                Response := Format('服务器错误。(%s)',[routComm.SOSTATEMENT]);
              end;
  else begin
         Result := False;
         Response := Format('服务器传回错误。(%s)',[routComm.SOSTATEMENT]);
       end;
  end;
end;

procedure TTeller.InitTeller(ZoneNo, Brno, Teller: String; Rank, Status: Integer;WorkStatus:Boolean);
begin
  CurTeller.ZONENO := ZoneNo;
  CurTeller.BRANCH := Brno;
  CurTeller.TELLER := Teller;
  CurTeller.RANK := Rank;
  CurTeller.STATUS := Status;
  CurTeller.WorkStatus := WorkStatus;

  If WorkStatus then
  begin
    Label2.Caption := StringOfChar('0', 5 - Length(CurTeller.ZONENO)) + CurTeller.ZONENO;
    Label9.Caption := StringOfChar('0', 5 - Length(CurTeller.BRANCH)) + CurTeller.BRANCH;
    Label3.Caption := StringOfChar('0', 5 - Length(CurTeller.TELLER)) + CurTeller.TELLER;
    GroupBox1.Caption := '终端信息（联机状态）';
  end else begin
    Label2.Caption := '[未签到]';
    Label9.Caption := '[未签到]';
    Label3.Caption := '[未签到]';
    GroupBox1.Caption := '终端信息（脱机状态）';
  end;
end;

procedure TTeller.InitSetting;
var
  FileName:String;
  ini:TiniFile;
begin
  FileName:=ChangeFileExt(Application.ExeName,'.ini');
  ini:=TiniFile.Create(FileName);

  StartupParam.HostAddr :=
               ini.ReadString('HOST','HostAddr','127.0.0.1');
  StartupParam.HostPort :=
               ini.ReadString('HOST','HostPort','1111');

  StartupParam.RequestHead :=
               ini.ReadString('TRCT','TransType','TRCT');
  StartupParam.ProcessHead :=
               ini.ReadString('TRPT','TransType','TRPT');

  StartupParam.Internal :=
               ini.ReadString('RETTYPE','Internal','Update');
  StartupParam.Normal :=
               ini.ReadString('RETTYPE','Normal','Normal');
  StartupParam.Abnormal :=
               ini.ReadString('RETTYPE','Abnormal','Abnormal');

  StartupParam.ZONE :=
               ini.ReadString('ZONE','ZONENO','0000');

  ini.Free;

  InitTeller('0000','0000','0000',0,0,OFFLINE);
end;

function TTeller.Run(LibId, WindowId:String;sinComm:TSinComm):TSoutComm;
var
   HInst: THandle;
   HProc: procedure(x:TSinComm;var y:TSoutComm);stdcall;
   LibName, ProcName: String;
   TrxCode, TrxInputHead, TrxInputFields: String;
begin
   If (LibId <> '')and(WindowId <>'') then
   begin
      HInst := LoadLibrary(PChar(LibId));
      Try
         If HInst <> 0 then
         begin
            HProc := GetProcAddress(HInst, PChar(WindowId));
            Self.Visible := False;
            If @HProc <> nil then
            begin
               HProc(sinComm, Result);
            end else begin
               Result.SOOVERID := RONOTFOUNDWID;
               Result.SOSTATEMENT := Format(ROSTATNOTFOUNDWID+'[%s]',[WindowId]);
            end;
            Self.Visible := True;
            FreeLibrary(HInst);
         end else begin
            Result.SOOVERID := RONOTFOUNDLID;
            Result.SOSTATEMENT := Format(ROSTATNOTFOUNDLID+'[%s]',[LibId]);
         end;
      except
         Result.SOOVERID := ROWRONGTRXWID;
         Result.SOSTATEMENT := Format(ROSTATWRONGTRXWID+'[%s]',[sinComm.SITRXD]);
      end;
   end;
end;

function TTeller.SoutAnalysis(soutComm: TSoutComm;var Dest:String):Boolean;
var
   OVERID: Integer;
   STATEMENT: String;
begin
   Try
      OVERID := StrToInt(soutComm.SOOVERID);
   except
      Dest := soutComm.SOOVERID;
      Result := False;
      Exit;
   end;
   STATEMENT := soutComm.SOSTATEMENT;
   Case OVERID of
      SID002 : begin
                 Try
                    If soutComm.SOFIELD04 = 'ONLINE' then
                    begin
                       InitTeller(soutComm.SOFIELD05,
                                  soutComm.SOFIELD01,
                                  soutComm.SOFIELD00,
                                  StrToInt(soutComm.SOFIELD03),
                                  StrToInt(soutComm.SOFIELD02),
                                  ONLINE);
                    end else If soutComm.SOFIELD04 = 'OFFLINE' then
                    begin
                       InitTeller('0000','0000','0000',0,0,OFFLINE);
                    end;

                    Dest := ROSTATNORMAL;
                    Result := True;
                 except
                    Dest := '数据接收错误，类型不符。';
                    Result := False;
                 end;
               end;
      SID000 : begin
                 Dest := STATEMENT;
                 Result := True;
               end;
      SID001 : begin
                 Dest := STATEMENT;
                 Result := False;
               end;
      SID004 : begin
                 Dest := STATEMENT;
                 Result := False;
               end;
      else begin
         Dest := Format('服务器返回错误信息 [%s]',[STATEMENT]);
         Result := False;
      end;
   end;
   Result := True;
end;

function TTeller.IsReady:Boolean;
begin
  If Edit1.Text='' then
  begin
    ShowMessage('请输入“交易代码”');
    Edit1.SelectAll;
    Edit1.SetFocus;
    Result:=False;
  end else begin
    Result:=True;
  end;
end;

procedure TTeller.BitBtn1Click(Sender: TObject);
var
  AVAILABLE, STATEMENT, TRXSQNO, LIBID, WINDOWID, SYSDATE:String;
  RACS: String;
  RequestCheck, OBuf: String;
  routComm: TSoutComm;
begin
  BitBtn1.Enabled := False;
  BitBtn2.Enabled := False;

  If IsReady then
  begin
    Try   { 服务交易请求检验过程 }
       routComm := RAC(Edit1.Text);
    except
       Exit;
    end;
    Try
       If RoutAnalysis(routComm, RACS) then
       begin { 服务交易请求执行过程 }
          RACS := RAN(routComm);
       end;
    except
       Exit;
    end;
    ShowMessage(RACS);

    Edit1.SelectAll;
    Edit1.SetFocus;
  end;

  BitBtn1.Enabled := True;
  BitBtn2.Enabled := True;
end;

procedure TTeller.BitBtn2Click(Sender: TObject);
begin
  Close;
end;

procedure TTeller.FormCreate(Sender: TObject);
begin
  Caption:=Title;
  Journal:=TStringList.Create;
  InitSetting;
end;

procedure TTeller.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  If CurTeller.WorkStatus then
  begin
    ShowMessage('不能关闭，您还没有“签退”');
    Edit1.SelectAll;
    Edit1.SetFocus;
    CanClose:=False;
  end else begin
    CanClose:=True;
  end;
end;

procedure TTeller.FormDestroy(Sender: TObject);
begin
  If Journal.Count>0 then
  begin
    If FileExists(LOGFILENAME) then DeleteFile(LOGFILENAME);
//    Journal.SaveToFile(LOGFILENAME);
//    Journal.Clear;
  end;
  Journal.Free;
end;

procedure TTeller.Log(ID,Description:String);
var
  LogItem:String;
begin
  LogItem:=FormatDateTime('[yyyy-mm-dd hh:mm:ss] ',Now)+ID+':'+Description;
  Journal.Add(LogItem);
end;

procedure TTeller.Timer1Timer(Sender: TObject);
begin
   Caption := '终端设备 @ ' + FormatDateTime('yyyy"年"mm"月"dd"日" hh:mm:ss ampm', Now);
end;

end.

