unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, internal, StdCtrls, IdBaseComponent, IdComponent,
  IdUDPBase, IdUDPServer, IdSocketHandle, ComCtrls, TabNotBk, IdUDPClient,
  dateutils;

Const
  ResultText : Array[0..15] of String = ('Okay',
                                        'Attempt to Open when already Open',
                                        'Cannot link to FSUIPC or WideClient',
                                        'Failed to Register common message with Windows',
                                        'Failed to create Atom for mapping filename',
                                        'Failed to create a file mapping object',
                                        'Failed to open a view to the file map',
                                        'Incorrect version of FSUIPC, or not FSUIPC',
                                        'Sim is not version requested',
                                        'Call cannot execute, link not Open',
                                        'Call cannot execute: no requests accumulated',
                                        'IPC timed out all retries',
                                        'IPC sendmessage failed all retries',
                                        'IPC request contains bad data',
                                        'Maybe running on WideClient, but FS not running on Server, or wrong FSUIPC',
                                        'Read or Write request cannot be added, memory for Process is full');
  SimulationText : Array[0..8] of String =
        ('Any', 'FS98', 'FS2K', 'CFS2', 'CFS1', 'FLY', 'FS2002','FS2004','FSX');

type
  TRemoteSim1 = class(TForm)
    Timer1: TTimer;
    udps: TIdUDPServer;
    TabbedNotebook1: TTabbedNotebook;
    Panel1: TPanel;
    labelsim: TLabel;
    labelfsuipcversion: TLabel;
    Panel2: TPanel;
    Panel3: TPanel;
    Panel4: TPanel;
    Panel5: TPanel;
    Panel6: TPanel;
    Panel7: TPanel;
    Panel8: TPanel;
    Panel9: TPanel;
    Panel10: TPanel;
    Panel15: TPanel;
    Edit1: TEdit;
    Label1: TLabel;
    Edit2: TEdit;
    Label2: TLabel;
    Bevel1: TBevel;
    udpfgc: TIdUDPClient;
    re1: TRichEdit;
    CheckBox1: TCheckBox;
    Panel11: TPanel;
    Panel12: TPanel;
    Panel13: TPanel;
    Panel14: TPanel;
    Panel16: TPanel;
    Button1: TButton;
    Button2: TButton;
    procedure Timer1Timer(Sender: TObject);
    procedure udpsUDPRead(Sender: TObject; AData: TStream;
      ABinding: TIdSocketHandle);
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
    function sendmouse_event(h1:hwnd;r:Trect;x,y:double):boolean;
    function getwindowpos(h1:hwnd):Trect;
    function GetFGCLeds(dc:hdC;h1:hwnd;r:Trect):string;
    function IsLedOn(DC:hDC;h1:hwnd;r:Trect;x,y:double;color:TColor;var panel:TPanel;
        cmdon,cmdoff:string;var status:boolean):char;
  public
    { Public declarations }
  end;

var
  RemoteSim1: TRemoteSim1;

var status:word;
var ti0, oldti:cardinal;
var
        hthread, hfsmain, hview, hmain, hthrust, hovhd, hcdu, hpedestal, hthrottle : hwnd;
                          rview, rmain, rthrust, rovhd, rcdu, rpedestal, rthrottle : Trect;
                          dcview, dcmain, dcthrust, dcovhd, dccdu, dcpedestal, dcthrottle : hDC;

implementation

{$R *.dfm}

//This needs to be called
//pMEM is a buffer of 32000 Byte
//FSUIPC_Open2(DWORD dwFSReq, DWORD *pdwResult, BYTE *pMem, DWORD dwSize);
var  Mem : array[0..32000] of byte;

function TRemoteSim1.sendmouse_event(h1:hwnd;r:Trect;x,y:double):boolean;
var x1,y1:integer; pt:TPoint; h2:hwnd;
begin
        x1:=round(r.left+x*(r.Right-r.Left));
        y1:=round(r.top+y*(r.bottom-r.top));
        GetCursorPos(pt);
        setforegroundwindow(hfsmain);
        SetCursorPos(x1,y1);
        mouse_event(mouseeventf_leftdown,0,0,0,0);
        GetDoubleClickTime;
        mouse_event(mouseeventf_leftup,0,0,0,0);
        SetCursorPos(0,0);
end;

procedure TRemoteSim1.Timer1Timer(Sender: TObject);
var
  dwResult : DWORD; st, key:string;
  auiTime  : Array[1..3] of Byte;
  com1, com2  : Array[1..2] of Byte;
  dwFSReq:word;
  dwSize:dword;
var rect:Trect; h1 : hwnd;
begin
        timer1.Enabled:=false;
        // Try to connect to FSUIPC (or WideFS)
        if (status=0) and
        (not FSUIPC_Open2(dwFSReq,dwResult,@mem,sizeof(mem))) then
        begin
                if dwresult>15 then dwresult:=2;
                Caption := 'Closed: '+ResultText[dwResult];
        end else
        //Here connect for the first time
        if status=0 then
        begin
                //key:='KZKAZ2A0JCQYFP-ERJ145.EXE'+chr(0);
                //FSUIPC_Write($8001, length(key), @key[1], dwResult);
                //FSUIPC_Process(dwResult);
                LabelSim.Caption := SimulationText[FSUIPC_FS_Version];
                LabelFSUIPCversion.Caption := Char(Ord('0') + ($0F and (FSUIPC_Version SHR 28))) +
                                  '.' +
                                  Char(Ord('0') + ($0F and (FSUIPC_Version SHR 24))) +
                                  Char(Ord('0') + ($0F and (FSUIPC_Version SHR 20))) +
                                  Char(Ord('0') + ($0F and (FSUIPC_Version SHR 16)));
                if (FSUIPC_Version and $FFFF) <> 0 then
                begin
                        LabelFSUIPCversion.Caption := LabelFSUIPCversion.Caption  +
                                    Char(Ord('a') + (FSUIPC_Version and $FF) - 1);
                end;

                hfsmain:= FindWindow('FS98MAIN',nil);
                hthread:= GetWindowThreadProcessID(hfsmain,nil);

                hview:=FindWindowEx(hfsmain,0,'FS98CHILD','COCKPIT - View 00');

                hmain:=FindWindowEx(hfsmain,0,'FS98CHILD','Main Panel');
                dcmain:=GetDC(hmain);

                hthrust:=FindWindowEx(hfsmain,0,'FS98CHILD','Thrust Rating');
                dcthrust:=GetDC(hThrust);

                h1:=FindWindow('FS98FLOAT','Overhead Panel');
                hovhd:=FindWindowEx(h1,0,'FS98CHILD','Overhead Panel');
                dcOvhd:=GetDC(hOvhd);

                h1:=FindWindow('FS98FLOAT','CDU 810');
                hcdu:=FindWindowEx(h1,0,'FS98CHILD','CDU 810');
                dccdu:=GetDC(hCdu);

                h1:=FindWindow('FS98FLOAT','Pedestal');
                hpedestal:=FindWindowEx(h1,0,'FS98CHILD','Pedestal');
                dcPedestal:=GetDC(hPedestal);

                h1:=FindWindow('FS98FLOAT','Throttle Quadrant');
                hthrottle:=FindWindowEx(h1,0,'FS98CHILD','Throttle Quadrant');
                dcThrottle:=GetDC(hThrottle);

                re1.lines.add('--- ERJ-145 Panel Interface Setup ---');
                if hthread >32 then re1.lines.add('FS Thread found: '+inttohex(hthread,6));
                if hfsmain >32 then re1.lines.add('FS98MAIN window found: '+inttohex(hfsmain,6));

                if hmain >32 then re1.lines.add('Main Panel found: '+inttohex(hmain,6)+'/'+inttohex(dcmain,6));
                if hthrust >32 then re1.lines.add('Thrust Panel found: '+inttohex(hthrust,6)+'/'+inttohex(dcthrust,6));
                if hovhd >32 then re1.lines.add('Overhead Panel found: '+inttohex(hovhd,6)+'/'+inttohex(dcovhd,6));
                if hcdu >32 then re1.lines.add('CDU810 found: '+inttohex(hcdu,6)+'/'+inttohex(dccdu,6));
                if hpedestal >32 then re1.lines.add('Pedestal found: '+inttohex(hpedestal,6)+'/'+inttohex(dcpedestal,6));
                if hthrottle >32 then re1.lines.add('Throttle Quadrant found: '+inttohex(hthrottle,6)+'/'+inttohex(dcthrottle,6));

                if (hthread<=32) or (hfsmain<=32) or (hmain<=32) or (hthrust<=32) or
                (hovhd<=32) or (hcdu<=32) or (hpedestal<=32) or (hthrottle<=32) then
                begin
                        re1.lines.add('*** Window(s) missing ***');
                        status:=0;
                end else
                begin
                        re1.lines.add('*** Enumeration complete ***');
                        status:=1;
                end;
                re1.lines.add('--- End of setup ---');
                caption:='Setup ok';
        end else
        //Advance the time status=1
        begin
               //Do nothing if hidden
                if remotesim1.Visible=true then
                begin
                //Update UTC
                FSUIPC_Read($023A, 3, @auiTime, dwResult);
                if FSUIPC_Process(dwResult) then
                begin
                        panel16.caption:=
                                Format('%.2d:%.2d:%.2d', [auiTime[2], auiTime[3], auiTime[1]]);
                end;
                end;
                rmain:=getwindowpos(hmain);
                remotesim1.GetFGCLeds(dcmain,hmain,rmain);
                caption:='Running';
        end;
        timer1.enabled:=true;
end;

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

{$I mainpanel.dat}
{$I fsuipc.inc}

//Result is Window Position in Screen Coordinates
function TRemoteSim1.getwindowpos(h1:hwnd):Trect;
var pwi:TWindowInfo;
begin
        getwindowinfo(h1,pwi);
        result:=pwi.rcWindow;
end;


//+++++++++++++++++++++++ Read LEDs ++++++++++++++++++++++++++++++


//Reads a LED hot spot and compares with color
//'1' if match => LED ON
function Tremotesim1.IsLedOn(DC:hDC;h1:hwnd;r:Trect;x,y:double;color:TColor;var panel:TPanel;
        cmdon,cmdoff:string;var status:boolean):char;
var tp:TPoint;color1:TColor;
begin
        result:='0';
        //calculate Screen Coordinates of LED Hotspot
        tp.x:=round(r.left+x*(r.Right-r.Left));
        tp.y:=round(r.top+y*(r.bottom-r.top));
        //convert to window coordinates
        windows.screentoclient(h1,tp);
        //Get the color
        color1:=GetPixel(DC,tp.x,tp.y);
        if color1=clr_invalid then
        begin
                panel.Color:=clred;
                exit;
        end;
        panel.color:=color1;
        if color1>=$002FFFFF then
        begin
                result:='1';
                if not status then
                begin
                        udpfgc.Send(cmdon);
                        sleep(100);
                end;
                status:=true;
        end else
        begin
                if status then
                begin
                        udpfgc.send(cmdoff);
                        sleep(100);
                end;
                status:=false;
        end;
end;

var bfd1,bhdg,bnav,bapr,bbnk,bap,bcpl,byd,bspd,bflc,bvs,balt,bfd2:boolean;

function TRemotesim1.GetFGCLeds(dc:hdC;h1:hwnd;r:Trect):string;
begin
        IsLEDOn(dc,h1,r,led_nav_x,led_nav_y,led_nav_color,panel5,'N','n',bnav);
        IsLEDOn(dc,h1,r,led_fd1_x,led_fd1_y,led_fd1_color,panel3,'F','f',bfd1);
        IsLEDOn(dc,h1,r,led_hdg_x,led_hdg_y,led_hdg_color,panel4,'H','h',bhdg);
        IsLEDOn(dc,h1,r,led_apr_x,led_apr_y,led_apr_color,panel6,'A','a',bapr);
        IsLEDOn(dc,h1,r,led_bnk_x,led_bnk_y,led_bnk_color,panel7,'B','b',bbnk);
        IsLEDOn(dc,h1,r,led_ap_x,led_ap_y,led_ap_color,panel8,'P','p',bap);
        IsLEDOn(dc,h1,r,led_cplleft_x,led_cplleft_y,led_cplleft_color,panel9,'D','d',bcpl);
        IsLEDOn(dc,h1,r,led_yd_x,led_yd_y,led_yd_color,panel10,'Y','y',byd);
        IsLEDOn(dc,h1,r,led_spd_x,led_spd_y,led_spd_color,panel11,'S','s',bspd);
        IsLEDOn(dc,h1,r,led_flc_x,led_flc_y,led_flc_color,panel12,'L','l',bflc);
        IsLEDOn(dc,h1,r,led_vs_x,led_vs_y,led_vs_color,panel15,'V','v',bvs);
        IsLEDOn(dc,h1,r,led_alt_x,led_alt_y,led_alt_color,panel13,'X','x',balt);
        IsLEDOn(dc,h1,r,led_fd2_x,led_fd2_y,led_fd2_color,panel14,'Z','z',bfd2);
end;


//++++++++++++++++++++++ Network Tokens +++++++++++++++++++++++++++

var active1:boolean=false;

procedure TRemoteSim1.udpsUDPRead(Sender: TObject; AData: TStream;
  ABinding: TIdSocketHandle);
var d:char;
  xxx,dwResult,altitude1, ttrk : DWORD;
  a:byte; i:integer;
  st1:array[1..2]of byte;
  p:array[0..255] of char;
  fast:boolean;
  hdg1,crs,spd:word;
  st:string;
  c:string;
var x,y:double;
    dti,ti:cardinal;
    vs:smallint;
    altitude,mach:double;
    mach1:integer;

begin
   if active1 then exit;
   ti:=GetTickCount-ti0;
   dti:=ti-oldti;
   oldti:=ti;
   i:=adata.read(p,sizeof(p));
   if i<>3 then exit;

   active1:=true;
   p[i]:=chr(0);
   c:=strpas(p);

   if checkbox1.Checked then
        re1.lines.add(inttostr(ti)+' - CMND:'+c+' - '+inttostr(dti));

   //Fast or slow
   if (dti)<100 then
        fast:=true else
        fast:=false;

   //Buttons
   //Flight Director ON/OFF
   if c = 'FD1' then
   begin
        rmain:=getwindowpos(hmain);
        sendmouse_event(hmain,rmain,fd1_x,fd1_y);
   end else
   if c = 'C1P' then
   begin
        rmain:=getwindowpos(hmain);
        sendmouse_event(hmain,rmain,fd1sync_x,fd1sync_y);
   end else
   if c = 'HDG' then
   begin
        rmain:=getwindowpos(hmain);
        sendmouse_event(hmain,rmain,hdg_x,hdg_y);
   end else
   if c = 'HDP' then
   begin
        rmain:=getwindowpos(hmain);
        sendmouse_event(hmain,rmain,hdgpush_x,hdgpush_y);
   end else
   if c = 'NAV' then
   begin
        rmain:=getwindowpos(hmain);
        sendmouse_event(hmain,rmain,nav_x,nav_y);
   end else
   if c = 'APR' then
   begin
        rmain:=getwindowpos(hmain);
        sendmouse_event(hmain,rmain,apr_x,apr_y);
   end else
   if c = 'BNK' then
   begin
        rmain:=getwindowpos(hmain);
        sendmouse_event(hmain,rmain,bnk_x,bnk_y);
   end else
   if c = 'APL' then
   begin
        rmain:=getwindowpos(hmain);
        sendmouse_event(hmain,rmain,ap_x,ap_y);
   end else
   if c = 'CPL' then
   begin
        rmain:=getwindowpos(hmain);
        sendmouse_event(hmain,rmain,cpl_x,cpl_y);
   end else
   if c = 'YDB' then
   begin
        rmain:=getwindowpos(hmain);
        sendmouse_event(hmain,rmain,yd_x,yd_y);
   end else
   if c = 'SPD' then
   begin
        rmain:=getwindowpos(hmain);
        sendmouse_event(hmain,rmain,spd_x,spd_y);
   end else
   if c = 'FLC' then
   begin
        rmain:=getwindowpos(hmain);
        sendmouse_event(hmain,rmain,flc_x,flc_y);
   end else
   if c = 'VSB' then
   begin
        rmain:=getwindowpos(hmain);
        sendmouse_event(hmain,rmain,vs_x,vs_y);
   end else
   if c = 'ALT' then
   begin
        rmain:=getwindowpos(hmain);
        sendmouse_event(hmain,rmain,alt_x,alt_y);
   end else
   if c = 'FD2' then
   begin
        rmain:=getwindowpos(hmain);
        sendmouse_event(hmain,rmain,fd2_x,fd2_y);
   end else
   if c = 'SPP' then
   begin
        rmain:=getwindowpos(hmain);
        sendmouse_event(hmain,rmain,spdpush_x,spdpush_y);
   end else
   if c = 'HDP' then
   begin
        rmain:=getwindowpos(hmain);
        sendmouse_event(hmain,rmain,hdgpush_x,hdgpush_y);
   end else
   if c = 'C1P' then
   begin
        rmain:=getwindowpos(hmain);
        sendmouse_event(hmain,rmain,fd1sync_x,fd1sync_y);
   end else

   //Dials
   if (c='HD+') or (c='HD-') then
   begin
        FSUIPC_Read(off_AP_HDG, 2, @hdg1, dwResult);
        FSUIPC_Process(dwResult);
        if not fast then
        begin
                if c='HD-' then hdg1:=hdg1-round(65536/360.0);
                if c='HD+' then hdg1:=hdg1+round(65536/360.0);
        end else
        begin
                if c='HD-' then hdg1:=hdg1-round(655360/360.0);
                if c='HD+' then hdg1:=hdg1+round(655360/360.0);
        end;
        if hdg1<0 then hdg1:=0;
        if hdg1>65535 then hdg1:=65535;
        FSUIPC_Write(off_AP_HDG, 2, @hdg1, dwResult);
        FSUIPC_Process(dwResult);
   end else
   //Old and OBSOLETE

   if (c ='AL-') or (c='AL+') then
   begin
        FSUIPC_Read(off_AP_ALT, 4, @altitude1, dwResult);
        FSUIPC_Process(dwResult);
        altitude:=round(altitude1/0.30479/65536.0);
        if not fast then
        begin
                if c='AL-' then altitude:=altitude-100;
                if c='AL+' then altitude:=altitude+100;
        end else
        begin
                if c='AL-' then altitude:=altitude-1000;
                if c='AL+' then altitude:=altitude+1000;
        end;
        if altitude<0 then altitude:=0;
        if altitude>37000 then altitude:=37000;
        altitude1:=round(altitude*0.30479*65536);
        FSUIPC_Write(off_AP_ALT, 4, @altitude1, dwResult);
        FSUIPC_Process(dwResult);
   end else

   if (c ='C1-') or (c='C1+') then
   begin
        FSUIPC_Read($C4E, 2, @crs, dwResult);
        FSUIPC_Process(dwResult);
        if not fast then
        begin
                if c='C1-' then crs:=crs-1;
                if c='C1+' then crs:=crs+1;
        end else
        begin
                if c='C1-' then crs:=crs-10;
                if c='C1+' then crs:=crs+10;
        end;
        if crs<0 then crs:=358;
        if crs>359 then crs:=0;
        FSUIPC_Write($C4E, 2, @crs, dwResult);
        FSUIPC_Process(dwResult);
   end else

   if (c ='SP-') or (c='SP+') then
   begin
        if bvs then
        begin
                FSUIPC_Read($7F2, 2, @vs, dwResult);
                FSUIPC_Process(dwResult);
                if not fast then
                begin
                        if c='SP-' then vs:=vs-100;
                        if c='SP+' then vs:=vs+100;
                end else
                begin
                        if c='SP-' then vs:=vs-500;
                        if c='SP+' then vs:=vs+500;
                end;
                if vs<-6000 then vs:=-6000;
                if vs>6000 then vs:=6000;
                FSUIPC_Write($7F2, 2, @vs, dwResult);
                FSUIPC_Process(dwResult);
        end else
        begin
                FSUIPC_Read($7E2, 2, @spd, dwResult);
                FSUIPC_Process(dwResult);
                if not fast then
                begin
                        if c='SP-' then spd:=spd-1;
                        if c='SP+' then spd:=spd+1;
                end else
                begin
                        if c='SP-' then spd:=spd-10;
                        if c='SP+' then spd:=spd+10;
                end;
                if spd<80 then spd:=80;
                if spd>330 then spd:=330;
                FSUIPC_Write($7E2, 2, @spd, dwResult);
                FSUIPC_Process(dwResult);
        end;
      {  if bspd then
        begin
                FSUIPC_Read($7E6, 4, @mach1, dwResult);
                FSUIPC_Process(dwResult);
                mach:=mach1/65536.0;
                if c='SP-' then mach:=mach-0.01;
                if c='SP+' then mach:=mach+0.01;
                if mach<0.2 then mach:=0.2;
                if mach>0.8 then mach:=0.8;
                mach1:=round(mach*65536.0);
                FSUIPC_Write($7E6, 2, @mach1, dwResult);
                FSUIPC_Process(dwResult);
        end;}

   end else

   {   if (c='C1+') or (c='C1-') then
   begin
        FSUIPC_Read(off_TTRK_4, 4, @ttrk, dwResult);
        FSUIPC_Process(dwResult);
        hdg1:=ttrk div 65536;
        FSUIPC_Write(off_AP_HDG, 2, @hdg1, dwResult);
        FSUIPC_Process(dwResult);
   end else}
   begin
   end;
   active1:=false;
end;

procedure TRemoteSim1.FormCreate(Sender: TObject);
begin
        tabbednotebook1.ActivePage:='System';
        ti0:=GetTickCount;
        oldti:=ti0;
end;

procedure TRemoteSim1.Button1Click(Sender: TObject);
begin
        re1.clear;
end;

procedure TRemoteSim1.Button2Click(Sender: TObject);
var  auiTime  : Array[1..3] of Byte; dwresult:dword; now1:TDateTIme;
begin
        now1:=now;
        auitime[1]:=secondof(now1);
        FSUIPC_Write($023A, 1, @auiTime[1], dwResult);
        auitime[1]:=minuteof(now1);
        FSUIPC_Write($023C, 1, @auiTime[1], dwResult);
        FSUIPC_Process(dwResult);
end;

end.
