unit CMach4Wrapper;

interface

uses
  SysUtils, shellApi,windows,Classes, Messages, forms,dialogs,stdCtrls,OleServer,ExtCtrls,
  sAlphaListBox,TlHelp32,

  machUtils, Mach4_TLB,MachUpdateThread,MachMsgLogFrame;

type
  TCMach4Wrapper = class(TCMach4Doc)
  private
    { Private declarations }
    Fconnected:boolean;
    FiMach:IMyScriptObject;
    FActive:boolean;
//    FReady:boolean;
    Fstopped:boolean;
    FMachLoc:string;
    FProfile:string;
    msgLog:TMachMsgLog;
    FMachWindowVisible,loaded:boolean;
    procedure setActive(value:boolean);
    procedure setConnected(value:boolean);
    function getReady:boolean;
    procedure setReady(value:boolean);
    procedure setMachWindowVisible(value:boolean);
    procedure setMachLoc(value:string);
    property Active:boolean read Factive write setActive default false;
  protected
    { Protected declarations }
  public
    { Public declarations }
    constructor create(owner:Tcomponent); override;
    destructor destroy; override;
    property connected:boolean read FConnected write setConnected;
    property ready:boolean read getReady write setReady;
    property iMach:IMyScriptObject read FiMach;
    function findMach:boolean;
    function findProfile:boolean;
    procedure InitiateControls(AOwner:TForm);
    procedure reset;
    procedure eStop;
    procedure Stop;
    procedure shutdown; overload;
    procedure Code(s:string);
    procedure Rewind;
    procedure Run;
    procedure Pause;
    procedure EditFile;
    procedure CloseFile;
    procedure LoadFile(fileName:string);
    procedure LoadRunFile(fileName:string);
    procedure ZeroAll;
    procedure ZeroX;
    procedure ZeroY;
    procedure ZeroZ;
    procedure ZeroA;
    procedure ZeroB;
    procedure ZeroC;
    procedure RefAll;
    procedure RefX;
    procedure RefY;
    procedure RefZ;
    procedure RefA;
    procedure RefB;
    procedure RefC;
    procedure FloodOn;
    procedure FloodOff;
    procedure FloodToggle;
    procedure MistOn;
    procedure MistOff;
    procedure MistToggle;
    procedure RegenToolPath;
    procedure G4(sec:double);
    procedure PauseFor(sec:double);
    procedure Absolute;
    procedure Incremental;
    procedure Message(s:String);
    procedure doSafeZ;
    function  SpindleClockwise:boolean;
    function  SpindleOn:boolean;
    function getStopped:boolean;
    function getVar(i:integer):double;
    procedure setVar(i:integer;d:double);
    function getOEMDro(i:integer):double;
    procedure setOEMDro(i:integer;d:double);
    function getOEMLED(i:integer):boolean;
    procedure doOEMbutton(i:integer);
    function isMoving:boolean;
    function IsProbing:boolean;
  published
    { Published declarations }
    property ConnectKind default ckNewInstance;
    property MachLoc:string read FMachLoc write setMachLoc ;
    property Profile:string read FProfile write FProfile ;
    property Stopped:boolean read getStopped write fStopped;
    property MachWindowVisible:boolean read FMachWindowVisible write setMachWindowVisible default True;
  public
    MachLoadWaitTime:Integer;
    Mach3WindowTitle:string;
    machPath:string;
    CodeText:TMemo;  // use as a pointer fot another form
    CodeList:TListbox;  // use as a pointer fot another form
    thrd:TMachUpdateThread;
  end;

function MachTitle(wHnd: THandle;lParam:LPARAM): Bool; stdcall;
function isrunning(s:string):boolean;
function Nextprogram(wnd:Thandle;list:Tstringlist):boolean;  stdcall;
function GetWindowExeName(Handle: THandle): String;
procedure Register;

implementation

uses
  MachDro,DROframe,DROAmountFrame,MachButton,MachMDIframe,MachLed,DroSpeedFrame,
  DROMultiBtn,MachJogDirButton,MachJogPctFrame,MachLabeledDro;

constructor TCMach4Wrapper.create(Owner:Tcomponent);
begin
  inherited create(Owner);
  connectKind:=ckNewInstance;
  fMachWindowvisible:=true;
end;

destructor TCMach4Wrapper.destroy;
begin
  inherited;
end;

procedure TCMach4Wrapper.setMachLoc(value:string);
begin
  FMachloc:=value;
  MachPath:=extractfilePath(value);
end;

procedure TCMach4Wrapper.setMachWindowVisible(value:boolean);
var
  hwnd:integer;
begin
  fMachWindowVisible:=value;
  if not loaded then
    exit;
  hWnd:=findWindow(nil,PChar(mach3WindowTitle));
  if hWnd=0 then
    raise Exception.Create('Mach not found');
  if value then
    showWindow(hWnd,SW_SHOWNOACTIVATE)
  else
    showWindow(hWnd,SW_HIDE);
end;

procedure TCMach4Wrapper.InitiateControls(AOwner:TForm);
var
  i:integer;
begin
  if self.iMach=nil then
    raise exception.Create('IMach not Set');
  if thrd=nil then
    thrd:=TMachUpdateThread.Create(true);
  for i:=0 to AOwner.ComponentCount-1 do
    if AOwner.Components[i].ClassType=TAxisDRO then
    begin
      thrd.components.Add(AOwner.components[i]);
      (AOwner.Components[i] as TAxisDRO).initiateControl(iMach,thrd);
    end else if AOwner.Components[i].ClassType=TDROAmount then
    begin
      thrd.components.Add(AOwner.components[i]);
      (AOwner.Components[i] as TDROAmount).initiateControl(iMach,thrd);
    end else if AOwner.Components[i].ClassType=TMachDRO then
    begin
      thrd.components.Add(AOwner.components[i]);
    end else if AOwner.Components[i].ClassType=TDROSpeed then
    begin
      thrd.components.Add(AOwner.components[i]);
      (AOwner.Components[i] as TDROSpeed).initiateControl(iMach,thrd);
    end else if AOwner.Components[i].ClassType=TJogPct then
    begin
      thrd.components.Add(AOwner.components[i]);
      (AOwner.Components[i] as TJogPct).initiateControl(iMach,thrd);
    end else  if AOwner.components[i].ClassType=TAxisMultiBtnDRO then
    begin
      thrd.components.Add(AOwner.components[i]);
      (AOwner.Components[i] as TAxisMultiBtnDRO).initiateControl(imach,thrd);
    end else  if AOwner.components[i].ClassType=TMachMsgLog then
    begin
      self.msgLog:=(AOwner.components[i] as TMachMsgLog);
    end else  if AOwner.components[i].ClassType=TMachLED then
    begin
      thrd.components.Add(AOwner.components[i]);
      (AOwner.Components[i] as TmachLED).iMach:=iMach;
    end else  if AOwner.components[i].ClassType=TDROSpeed then
    begin
      (AOwner.Components[i] as TDROSpeed).iMach:=iMach;
    end else  if AOwner.components[i].ClassType=TMachSpindleButton then
    begin
      thrd.components.Add(AOwner.components[i]);
      (AOwner.Components[i] as TMachSpindleButton).iMach:=iMach;
//      (AOwner.Components[i] as TMachSpindleButton).initiateControl(iMach,thrd);
    end else  if AOwner.components[i].ClassType=TMachMDI then
    begin
      thrd.components.Add(AOwner.components[i]);
      (AOwner.Components[i] as TMachMDI).iMach:=iMach;
    end else  if AOwner.Components[i].ClassType=TMAchESTOPButton then
    begin
      thrd.components.Add(AOwner.components[i]);
      (AOwner.Components[i] as TMachESTOPButton).iMach:=iMach;
    end else  if AOwner.Components[i].ClassType=TMachJogDirButton then
    begin
      (AOwner.Components[i] as TMachJogDirButton).iMach:=iMach;
    end else  if AOwner.Components[i].InheritsFrom(TMachToggleButton) then
    begin
      thrd.components.Add(AOwner.components[i]);
      (AOwner.Components[i] as TMachToggleButton).iMach:=iMach;
    end else  if AOwner.Components[i].InheritsFrom(TMachLabeledDRO) then
    begin
      thrd.components.Add(AOwner.components[i]);
      (AOwner.Components[i] as TMachLabeledDRO).iMach:=iMach;
    end else  if AOwner.Components[i].classType=TMachVBScriptButton then
    begin
      (AOwner.Components[i] as TMachButton).iMach:=iMach;
      (AOwner.Components[i] as TMachVBScriptButton).MacroDir:=MachPath+'Macros\'+profile;
      thrd.components.Add(AOwner.components[i]);
    end else  if AOwner.Components[i].InheritsFrom(TMachButton) then
    begin
      (AOwner.Components[i] as TMachButton).iMach:=iMach;
      thrd.components.Add(AOwner.components[i]);
    end;
end;

function TCMach4Wrapper.findMach:boolean;
var
  dlg:TOpenDialog;
begin
  result:=false;
  dlg:=TOpenDialog.Create(application);
  dlg.Options:=[ofFileMustExist];
  dlg.InitialDir:='.\';
  dlg.FileName:='Mach3.exe';
  if not dlg.Execute then
    exit;
  MachLoc:=dlg.FileName;
  freeAndNil(dlg);
  result:=true;
end;

function TCMach4Wrapper.findProfile:boolean;
var
  dlg:TOpenDialog;
begin
  result:=false;
  dlg:=TOpenDialog.Create(application);
  dlg.Filter:='MachProfile (*.xml)|*.XML';
  dlg.Options:=[ofFileMustExist];
  dlg.DefaultExt:='xml';
  dlg.InitialDir:=extractFilePath(machLoc);
  dlg.FileName:='*.xml';
  if not dlg.Execute then
    exit;
  FProfile:=extractfileName(dlg.FileName);
  FProfile:=copy(Fprofile,1,pos('.',fProfile)-1);
  freeAndNil(dlg);
  result:=true;
end;

procedure TCMach4Wrapper.setActive(value:boolean);
var
  hWnd:integer;
  sProfile:string;
  buff:array of AnsiChar;
begin
  hWnd:=0;
  FActive:=value;
  if value then
  begin
    if (FmachLoc='')or(not fileExists(FMachLoc)) then
      if not findMach then
      begin
        raise exception.Create('Unable to Find Mach3.exe');
        exit;
      end;
//    hWnd:=findWindow(nil,'Mach3 CNC Controller ');
//    hWnd:=findWindow(nil,'Mach3 Licensed To: Alan Kube ');
//    if hWnd=0 then
    if not isrunning('Mach3.exe')then
    begin
      if (FProfile='')or(not fileExists(extractFilePath(machloc)+FProfile+'.xml') )then
        if not findProfile then
        begin
          raise exception.Create('Unable to Find Mach Profile: "'+FProfile+'"');
          exit;
        end;
      sProfile:='/p "'+Fprofile+'"';
      hwnd:=shellExecute((owner as TForm).handle,'open',pChar(FmachLoc),pChar(sProfile),pChar('C:\mach3'),SW_SHOWNORMAL);
      sleep(MachLoadWaitTime);
      if hWnd<33 then
        raise exception.Create('Unable to start Mach: '+intToStr(hWnd));
      EnumWindows(@Machtitle,LPARAM(Self));
    end;
//    hWnd:=findWindow(nil,'Mach3 CNC Controller ');
//    hWnd:=findWindow(nil,'Mach3 CNC Licensed To: Alan Kube ');
//    if hWnd=0 then
    if not isRunning('Mach3.exe')then
      raise Exception.Create('Set Active: Mach not found');
    if not FMachWindowVisible then
      showWindow(hWnd,SW_HIDE);
    loaded:=true;
  end else
  begin
  end;
end;

procedure TCMach4Wrapper.setConnected(value:boolean);
var
  i:integer;
begin
  if value then
  begin
    if not active then
      active:=true;
    if not FConnected then
    begin
      try
        ConnectKind:=ckRunningInstance;
        Connect;
        try
          FiMach:=GetScriptDispatch as iMyScriptObject;
        except
          raise exception.Create('Unable to retrieve iMyScriptObject');
        end;
        fConnected:=true;
        InitiateControls(owner as TForm);
        thrd.Resume;
        ready:= not iMach.GetOEMLed(800);
      except
      end;
    end;
  end else
  begin
    if fConnected then
    begin
      try
        thrd.Terminate;
        application.ProcessMessages;
        sleep(500);
        freeAndNil(thrd);
        if not iMach.GetOEMLed(800) then // is online and active
        begin
          iMach.DeActivateSignal(7); //Output1
          iMach.DeActivateSignal(8); //Output2
          iMach.DeActivateSignal(9); //Output3
          iMach.DeActivateSignal(10); //Output4
          iMach.DoOEMButton(1021);
        end;
        ShutDown;  // works
      finally
        fConnected:=false;
        Active:=false;
      end;
    end;
  end;
end;

procedure TCMach4Wrapper.setReady(value:boolean);
begin
  if value then
    reset
  else
    estop;
end;

function TCMach4Wrapper.getReady:boolean;
begin
  result:=false;
  if connected then
    result:=not iMach.GetOEMLed(800);
end;

procedure TCMach4Wrapper.reset;
begin
  if not ready then
    imach.DoOEMButton(1021);
end;

procedure TCMach4Wrapper.eStop;
begin
  if ready then
    imach.DoOEMButton(1021);
end;

procedure TCMach4Wrapper.shutdown;
begin
  eStop;
  inherited shutdown;
end;

procedure TCMach4Wrapper.Stop;
begin
  imach.DoOEMButton(1003);
end;

procedure TCMach4Wrapper.Rewind;
begin
  imach.DoOEMButton(1002);
end;

procedure TCMach4Wrapper.Run;
begin
  imach.DoOEMButton(1000);
end;

procedure TCMach4Wrapper.Pause;
begin
  imach.DoOEMButton(1001);
end;

procedure TCMach4Wrapper.EditFile;
begin
  imach.DoOEMButton(1006);
end;

procedure TCMach4Wrapper.CloseFile;
begin
  imach.DoOEMButton(169);
end;

procedure TCMach4Wrapper.LoadFile(fileName:string);
begin
  closefile;
  sleep(250);
  imach.LoadFile(filename);
  if codeText<>nil then
    codeText.Lines.LoadFromFile(filename);
  if codeList<>nil then
    codeList.items.LoadFromFile(filename);
end;

procedure TCMach4Wrapper.LoadRunFile(fileName:string);
begin
  closeFile;
  sleep(250);
  imach.LoadRun(FileName);
end;

procedure TCMach4Wrapper.ZeroAll;
begin
  imach.DoOEMButton(1007);
end;

procedure TCMach4Wrapper.ZeroX;
begin
  imach.DoOEMButton(1008);
end;

procedure TCMach4Wrapper.ZeroY;
begin
  imach.DoOEMButton(1009);
end;

procedure TCMach4Wrapper.ZeroZ;
begin
  imach.DoOEMButton(1010);
end;

procedure TCMach4Wrapper.ZeroA;
begin
  imach.DoOEMButton(1011);
end;

procedure TCMach4Wrapper.ZeroB;
begin
  imach.DoOEMButton(1012);
end;

procedure TCMach4Wrapper.ZeroC;
begin
  imach.DoOEMButton(1013);
end;

procedure TCMach4Wrapper.RefAll;
begin
  refZ;
  refY;
  refX;
  refA;
  refB;
  refC;
end;

procedure TCMach4Wrapper.RefX;
begin
  imach.DoOEMButton(1022);
end;

procedure TCMach4Wrapper.RefY;
begin
  imach.DoOEMButton(1023);
end;

procedure TCMach4Wrapper.RefZ;
begin
  imach.DoOEMButton(1024);
end;

procedure TCMach4Wrapper.RefA;
begin
  imach.DoOEMButton(1025);
end;

procedure TCMach4Wrapper.RefB;
begin
  imach.DoOEMButton(1026);
end;

procedure TCMach4Wrapper.RefC;
begin
  imach.DoOEMButton(1027);
end;

procedure TCMach4Wrapper.FloodOn;
begin
  imach.DoOEMButton(224);
end;

procedure TCMach4Wrapper.FloodOff;
begin
  imach.DoOEMButton(225);
end;

procedure TCMach4Wrapper.FloodToggle;
begin
  imach.DoOEMButton(113);
end;

procedure TCMach4Wrapper.MistOn;
begin
  imach.DoOEMButton(226);
end;

procedure TCMach4Wrapper.MistOff;
begin
  imach.DoOEMButton(227);
end;

procedure TCMach4Wrapper.MistToggle;
begin
  imach.DoOEMButton(114);
end;

procedure TCMach4Wrapper.RegenToolPath;
begin
  imach.DoOEMButton(160);
end;

procedure TCMach4Wrapper.G4(sec:double);
begin
  imach.code('G4 P'+cstr(sec));
end;

procedure TCMach4Wrapper.code(s:string);
begin
  imach.Code(s);
end;

procedure TCMach4Wrapper.Message(s:String);
begin
  imach.Message(s);
  if msgLog<>nil then
    msgLog.msg.Lines.Add(s);
end;

function TCMach4Wrapper.getVar(i:integer):double;
begin
  result:=iMach.GetVar(i);
end;

procedure TCMach4Wrapper.setVar(i:integer;d:double);
begin
  imach.SetVar(i,d);
end;

function TCMach4Wrapper.getOEMDro(i:integer):double;
begin
  result:=iMach.GetOEMDRO(i);
end;

procedure TCMach4Wrapper.setOEMDro(i:integer;d:double);
begin
  imach.SetOEMDRO(i,d);
end;

function TCMach4Wrapper.getOEMLED(i:integer):boolean;
begin
  result:=iMach.GetOEMLed(i);
end;

procedure TCMach4Wrapper.doOEMbutton(i:integer);
begin
  iMach.DoOEMButton(i);
end;

procedure TCMach4Wrapper.doSafeZ;
begin
  iMach.DoOEMButton(104);
end;

function  TCMach4Wrapper.SpindleClockwise:boolean;
begin
  result:=getOemLed(11);
end;

function  TCMach4Wrapper.SpindleOn:boolean;
begin
  result:=getOEMLed(116);
end;

function TCMach4Wrapper.getStopped:boolean;
begin
  result:=false;
  if imach= nil then
    exit;
  result:=imach.GetOEMLed(80);
end;

procedure TCMach4Wrapper.PauseFor(sec:double);
begin
  G4(sec);
end;

procedure TCMach4Wrapper.Absolute;
begin
  iMach.Code('G90');
end;

procedure TCMach4Wrapper.Incremental;
begin
  iMach.Code('G91');
end;

function TCMach4Wrapper.isMoving:boolean;
begin
  result:=iMach.isMoving<>0;
end;

function TCMach4Wrapper.IsProbing:boolean;
begin
  result:=iMach.IsProbing<>0;
end;

procedure Register;
begin
  RegisterComponents('Mach', [TCMach4Wrapper]);
end;

function isrunning(s:string):boolean;
{Calls EnumWindows to check for a specific program,
 returns boolean value  True if found, False otherwise}
var
  i:integer;
  proglist:Tstringlist;
begin
  result:=false;
  proglist:=Tstringlist.create;
  try
    enumwindows(@nextprogram,lparam(proglist)); {pass the list as a parameter}
    with proglist do
    for i:=0 to proglist.count-1 do
    if comparetext(s,proglist[i])= 0 then
    begin
      result:=true;
      break;
    end;
  finally
    proglist.free;
  end;
end;

function Nextprogram(wnd:Thandle;list:Tstringlist):boolean;  stdcall;
{This is the callback function which is called by EnumWindows procedure for each
 top-level window.  Return "true" to keep retrieving, return "false" to stop
 EnumWindows  from calling}
var s:string;
begin
  s:=getWindowexename(wnd);
  if list.indexof(s)<0 then list.add(s);
  result:=true;
end;

function GetWindowExeName(Handle: THandle): String;
{Given a window handlew, return the program name}
var
 PE: TProcessEntry32;
 Snap: THandle;
 ProcessId: cardinal;
begin
  result:='???';
  pe.dwsize:=sizeof(PE);
 GetWindowThreadProcessId(Handle,@ProcessId);
 Snap:= CreateToolHelp32Snapshot(TH32CS_SNAPPROCESS, 0);
 if Snap <> 0 then
 begin
   if Process32First(Snap, PE) then
     if PE.th32ProcessID = ProcessId then
       Result:= String(PE.szExeFile)
     else while Process32Next(Snap, PE) do
       if PE.th32ProcessID = ProcessId then
       begin
         Result:= String(PE.szExeFile);
         break;
       end;
   CloseHandle(Snap);
 end;
end;

function MachTitle(wHnd: THandle;lParam:LPARAM): Bool; stdcall;
  var Buffer: array[0..255] of char;
begin
    result:=False;
    SendMessage(wHnd, WM_GETTEXT, 255, LongInt(@Buffer[0]));
    if (Buffer <> '') and IsWindow(wHnd) then
      begin
        if Pos('Mach3 ',Buffer)<>0 then
        begin
          TCMach4Wrapper(lParam).mach3WindowTitle:=Buffer;
          Exit;
        end;
      end;
    Result := True; // continiue enumeration
end;

end.
